GithubHelp home page GithubHelp logo

isolated-vm's Introduction

npm version isc license github action npm downloads

isolated-vm -- Access to multiple isolates in nodejs

NPM

isolated-vm is a library for nodejs which gives you access to v8's Isolate interface. This allows you to create JavaScript environments which are completely isolated from each other. This can be a powerful tool to run code in a fresh JavaScript environment completely free of extraneous capabilities provided by the nodejs runtime.

PROJECT STATUS

isolated-vm is currently in maintenance mode. New features are not actively being added but existing features and new versions of nodejs are supported as possible. There are some major architectural changes which need to be added to improve the stability and security of the project. I don't have as much spare time as I did when I started this project, so there is not currently any plan for these improvements.

Wishlist

  1. Multi-process architecture. v8 is not resilient to out of memory conditions and is unable to gracefully unwind from these errors. Therefore it is possible, and even common, to crash a process with poorly-written or hostile software. I implemented a band-aid for this with the onCatastrophicError callback which quarantines a corrupted isolate, but it is not reliable.

  2. Bundled v8 version. nodejs uses a patched version of v8 which makes development of this module more difficult than it needs to be. For some reason they're also allowed to change the v8 ABI in semver minor releases as well, which causes issues for users while upgrading nodejs. Also, some Linux distributions strip "internal" symbols from their nodejs binaries which makes usage of this module impossible. I think the way to go is to compile and link against our own version of v8.

CONTENTS

REQUIREMENTS

This project requires nodejs version 16.x (or later).

🚨 If you are using a version of nodejs 20.x or later, you must pass --no-node-snapshot to node.

Furthermore, to install this module you will need a compiler installed. If you run into errors while running npm install isolated-vm it is likely you don't have a compiler set up, or your compiler is too old.

  • Windows + OS X users should follow the instructions here: node-gyp
  • Ubuntu users should run: sudo apt-get install python g++ build-essential
  • Alpine users should run: sudo apk add python3 make g++
  • Amazon Linux AMI users should run: sudo yum install gcc72 gcc72-c++
  • Arch Linux users should run: sudo pacman -S make gcc python
  • Red Hat users should run: sudo dnf install python3 make gcc gcc-c++ zlib-devel brotli-devel openssl-devel

WHO IS USING ISOLATED-VM

  • Screeps - Screeps is an online JavaScript-based MMO+RPG game. They are using isolated-vm to run arbitrary player-supplied code in secure environments which can persistent for several days at a time.

  • Fly - Fly is a programmable CDN which hosts dynamic endpoints as opposed to just static resources. They are using isolated-vm to run globally distributed applications, where each application may have wildly different traffic patterns.

  • Algolia - Algolia is a Search as a Service provider. They use isolated-vm to power their Custom Crawler product, which allows them to safely execute user-provided code for content extraction.

  • Tripadvisor - Tripadvisor is the world’s largest travel platform. They use isolated-vm to server-side render thousands of React pages per second.

SECURITY

Running untrusted code is an extraordinarily difficult problem which must be approached with great care. Use of isolated-vm to run untrusted code does not automatically make your application safe. Through carelessness or misuse of the library it can be possible to leak sensitive data or grant undesired privileges to an isolate.

At a minimum you should take care not to leak any instances of isolated-vm objects (Reference, ExternalCopy, etc) to untrusted code. It is usually trivial for an attacker to use these instances as a springboard back into the nodejs isolate which will yield complete control over a process.

Additionally, it is wise to keep nodejs up to date through point releases which affect v8. You can find these on the nodejs changelog by looking for entries such as "update V8 to 9.1.269.36 (Michaël Zasso) #38273". Historically there have usually been 3-5 of these updates within a single nodejs LTS release cycle. It is not recommended to use odd-numbered nodejs releases since these frequently break ABI and API compatibility and isolated-vm doesn't aim to be compatible with bleeding edge v8.

v8 is a relatively robust runtime, but there are always new and exciting ways to crash, hang, exploit, or otherwise disrupt a process with plain old JavaScript. Your application must be resilient to these kinds of issues and attacks. It's a good idea to keep instances of isolated-vm in a different nodejs process than other critical infrastructure.

If advanced persistent threats are within your threat model it's a very good idea to architect your application using a foundation similar to Chromium's site isolation. You'll also need to make sure to keep your system kernel up to date against local privilege escalation attacks. Running your service in a container such as a Docker may be a good idea but it is important to research container escape attacks as well.

API DOCUMENTATION

Since isolates share no resources with each other, most of this API is built to provide primitives which make marshalling data between many isolates quick and easy. The only way to pass data from one isolate to another is to first make that data transferable. Primitives (except for Symbol) are always transferable. This means if you invoke a function in a different isolate with a number or string as the argument, it will work fine. If you need to pass more complex information you will have to first make the data transferable with one of the methods here.

Most methods will provide both a synchronous and an asynchronous version. Calling the synchronous functions will block your thread while the method runs and eventually returns a value. The asynchronous functions will return a Promise while the work runs in a separate thread pool.

There are some rules about which functions may be called from certain contexts:

  1. Asynchronous functions may be called at any time
  2. Synchronous functions usually may not be called from an asynchronous function
  3. You may call a synchronous function from an asynchronous function as long as that function belongs to current isolate
  4. You may call a synchronous function belonging to the default nodejs isolate at any time

Additionally, some methods will provide an "ignored" version which runs asynchronously but returns no promise. This can be a good option when the calling isolate would ignore the promise anyway, since the ignored versions can skip an extra thread synchronization. Just be careful because this swallows any thrown exceptions which might make problems hard to track down.

It's also worth noting that all asynchronous invocations will run in the order they were queued, regardless of whether or not you wait on them. So, for instance, you could call several "ignored" methods in a row and then await on a final async method to observe some side-effect of the ignored methods.

Class: Isolate [transferable]

This is the main reference to an isolate. Every handle to an isolate is transferable, which means you can give isolates references to each other. An isolate will remain valid as long as someone holds a handle to the isolate or anything created inside that isolate. Once an isolate is lost the garbage collector should eventually find it and clean up its memory. Since an isolate and all it contains can represent quite a large chunk of memory though you may want to explicitly call the dispose() method on isolates that you are finished with to get that memory back immediately.

new ivm.Isolate(options)
  • options [object]
    • memoryLimit [number] - Memory limit that this isolate may use, in MB. Note that this is more of a guideline instead of a strict limit. A determined attacker could use 2-3 times this limit before their script is terminated. Against non-hostile code this limit should be pretty close. The default is 128MB and the minimum is 8MB.
    • inspector [boolean] - Enable v8 inspector support in this isolate. See inspector-example.js in this repository for an example of how to use this.
    • snapshot [ExternalCopy[ArrayBuffer]] - This is an optional snapshot created from createSnapshot which will be used to initialize the heap of this isolate.
    • onCatastrophicError [function] - Callback to be invoked when a very bad error occurs. If this is invoked it means that v8 has lost all control over the isolate, and all resources in use are totally unrecoverable. If you receive this error you should log the error, stop serving requests, finish outstanding work, and end the process by calling process.abort().
ivm.Isolate.createSnapshot(scripts, warmup_script)
  • scripts [array]
  • warmup_script [string] - Optional script to "warmup" the snapshot by triggering code compilation

🚨 You should not use this feature. It was never all that stable to begin with and has grown increasingly unstable due to changes in v8.

Note: createSnapshot does not provide the same isolate protection like the rest of isolated-vm. If the script passed to createSnapshot uses too much memory the process will crash, and if it has an infinite loop it will stall the process. Furthermore newer v8 features may simply fail when attempting to take a snapshot that uses them. It is best to snapshot code that only defines functions, class, and simple data structures.

isolate.compileScript(code) Promise
isolate.compileScriptSync(code)

Note that a Script can only run in the isolate which created it.

isolate.compileModule(code) Promise
isolate.compileModuleSync(code)
  • code [string] - The JavaScript code to compile.

  • options [object]

    • meta [function] - Callback which will be invoked the first time this module accesses import.meta. The meta object will be passed as the first argument. This option may only be used when invoking compileModule from within the same isolate.
  • return A Module object.

Note that a Module can only run in the isolate which created it.

isolate.createContext() Promise
isolate.createContextSync()
  • options [object]

    • inspector [boolean] - Enable the v8 inspector for this context. The inspector must have been enabled for the isolate as well.
  • return A Context object.

isolate.dispose()

Destroys this isolate and invalidates all references obtained from it.

isolate.getHeapStatistics() Promise
isolate.getHeapStatisticsSync()
  • return [object]

Returns heap statistics from v8. The return value is almost identical to the nodejs function v8.getHeapStatistics(). This function returns one additional property: externally_allocated_size which is the total amount of currently allocated memory which is not included in the v8 heap but counts against this isolate's memoryLimit. ArrayBuffer instances over a certain size are externally allocated and will be counted here.

isolate.cpuTime bigint
isolate.wallTime bigint

The total CPU and wall time spent in this isolate, in nanoseconds. CPU time is the amount of time the isolate has spent actively doing work on the CPU. Wall time is the amount of time the isolate has been running, including passive time spent waiting (think "wall" like a clock on the wall). For instance, if an isolate makes a call into another isolate, wall time will continue increasing while CPU time will remain the same.

Note that in nodejs v10.x the return value is a regular number, since bigint isn't supported on earlier versions.

Also note that CPU time may vary drastically if there is contention for the CPU. This could occur if other processes are trying to do work, or if you have more than require('os').cpus().length isolates currently doing work in the same nodejs process.

isolate.isDisposed [boolean]

Flag that indicates whether this isolate has been disposed.

isolate.referenceCount [number]

Returns the total count of active Reference instances that belong to this isolate. Note that in certain cases many Reference instances in JavaScript will point to the same underlying reference handle, in which case this number will only reflect the underlying reference handle. This happens when you transfer a Reference instance via some method which accepts transferable values. This will also include underlying reference handles created by isolated-vm like Script or Context objects.

isolate.startCpuProfiler(title) [void]

Start a CPU profiler in the isolate, for performance profiling. It only collects cpu profiles when the isolate is active in a thread.

isolate.stopCpuProfiler(title) [Promise<Array>]

Stop a CPU profiler previously started using the same title. It returns an array of profiles dependening on how many times the isolate get activated in a thread.

Class: Context [transferable]

A context is a sandboxed execution environment within an isolate. Each context contains its own built-in objects and global space.

context.global Reference

Reference to this context's global object. Note that if you call context.release() the global reference will be released as well.

context.eval(code, options) Promise
context.evalIgnored(code, options)
context.evalSync(code, options)
  • code [string] - The code to run
  • options [object]
  • return [transferable]

Compiles and executes a script within a context. This will return the last value evaluated, as long as that value was transferable, otherwise undefined will be returned.

context.evalClosure(code, arguments, options) Promise
context.evalClosureIgnored(code, arguments, options)
context.evalClosureSync(code, arguments, options)
  • code [string] - The code to run
  • arguments *[array]` - Arguments to pass to this code
  • options [object]
  • return `[transferable]

Compiles and runs code as if it were inside a function, similar to the seldom-used new Function(code) constructor. You can pass arguments to the function and they will be available as $0, $1, and so on. You can also use return from the code.

context.release()

Releases this reference to the context. You can call this to free up v8 resources immediately, or you can let the garbage collector handle it when it feels like it. Note that if there are other references to this context it will not be disposed. This only affects this reference to the context.

Class: Script [transferable]

A script is a compiled chunk of JavaScript which can be executed in any context within a single isolate.

script.release()

Releases the reference to this script, allowing the script data to be garbage collected. Functions and data created in the isolate by previous invocations to script.run(...) will still be alive in their respective contexts-- this only means that you can't invoke script.run(...) again with this reference.

script.run(context, options) Promise
script.runIgnored(context, options)
script.runSync(context, options)
  • context Context - The context in which this script will run.
  • options [object]
    • release [boolean] - If true release() will automatically be called on this instance.
    • timeout [number] - Maximum amount of time in milliseconds this script is allowed to run before execution is canceled. Default is no timeout.
    • { ...TransferOptions }
  • return [transferable]

Runs a given script within a context. This will return the last value evaluated in a given script, as long as that value was transferable, otherwise undefined will be returned. For instance if your script was "let foo = 1; let bar = 2; bar = foo + bar" then the return value will be 3 because that is the last expression.

Class: Module [transferable]

A JavaScript module. Note that a Module can only run in the isolate which created it.

module.dependencySpecifiers

A read-only array of all dependency specifiers the module has.

	const code = `import something from './something';`;
	const module = await isolate.compileModule(code);
	const dependencySpecifiers = module.dependencySpecifiers;
	// dependencySpecifiers => ["./something"];
module.namespace

Returns a Reference containing all exported values.

module.instantiate(context, resolveCallback) Promise
module.instantiateSync(context, resolveCallback)
  • context Context - The context the module should use.
  • resolveCallback - This callback is responsible for resolving all direct and indirect dependencies of this module. It accepts two parameters: specifier and referrer. It must return a Module instance which will be used to satisfy the dependency. The asynchronous version of instantiate may return a promise from resolveCallback.

Instantiate the module together with all its dependencies. Calling this more than once on a single module will have no effect.

module.evaluate(options) Promise
module.evaluateSync(options)
  • options [object] - Optional.
    • timeout [number] - Maximum amount of time in milliseconds this module is allowed to run before execution is canceled. Default is no timeout.
  • return [transferable]

Evaluate the module and return the last expression (same as script.run). If evaluate is called more than once on the same module the return value from the first invocation will be returned (or thrown).

Note: nodejs v14.8.0 enabled top-level await by default which has the effect of breaking the return value of this function.

module.release()

Releases this module. This behaves the same as other .release() methods.

Class: Callback [transferable]

Callbacks can be used to create cross-isolate references to simple functions. This can be easier and safer than dealing with the more flexible Reference class. Arguments passed to and returned from callbacks are always copied using the same method as ExternalCopy. When transferred to another isolate, instances of Callback will turn into a plain old function. Callbacks are created automatically when passing functions to most isolated-vm functions.

new ivm.Callback(fn, options)
  • options [object]
    • async [boolean] - Function will invoke the callback in "async" mode, which immediately returns a promise.
    • ignored [boolean] - Function will invoke the callback in "ignored" mode, which immediately returns undefined and ignores the result of the function (including thrown exceptions)
    • sync [boolean] - Function will invoke the callback in "sync" mode, blocking for a response (default).

Class: Reference [transferable]

A instance of Reference is a pointer to a value stored in any isolate.

new ivm.Reference(value, options)
  • value - The value to create a reference to.
  • options [object]
    • unsafeInherit [boolean] - If enabled then the get family of functions will follow the object's prototype chain. References created with this option should never be given to untrusted code.
reference.typeof [string]

This is the typeof the referenced value, and is available at any time from any isolate. Note that this differs from the real typeof operator in that null is "null", and Symbols are "object".

reference.copy() Promise
reference.copySync()
  • return JavaScript value of the reference.

Creates a copy of the referenced value and internalizes it into this isolate. This uses the same copy rules as ExternalCopy.

reference.deref()
  • options [object]
    • release [boolean] - If true release() will automatically be called on this instance.
  • return The value referenced by this handle.

Will attempt to return the actual value or object pointed to by this reference. Note that in order to call this function the reference must be owned by the current isolate, otherwise an error will be thrown.

reference.derefInto()
  • options [object]
    • release [boolean] - If true release() will automatically be called on this instance.
  • return [transferable]

Returns an object, which when passed to another isolate will cause that isolate to dereference the handle.

reference.release()

Releases this reference. If you're passing around a lot of references between isolates it's wise to release the references when you are done. Otherwise you may run into issues with isolates running out of memory because other isolates haven't garbage collected recently. After calling this method all attempts to access the reference will throw an error.

reference.delete(property) Promise
reference.deleteIgnored(property)
reference.deleteSync(property)
  • property [transferable] - The property to access on this object.

Delete a property from this reference, as if using delete reference[property]

reference.get(property, options) Promise
reference.getSync(property, options)
  • property [transferable] - The property to access on this object.
  • options [object]
    • accessors [boolean] - Whether or not to invoke accessors and proxies on the underlying object. Note that there is no way to supply a timeout to this function so only use this option in trusted code.
    • { ...TransferOptions }
  • return A Reference object.

Will access a reference as if using reference[property] and transfer the value out.

If the object is a proxy, or if the property is a getter, this method will throw unless the accessors option is true.

reference.set(property, value, options) Promise
reference.setIgnored(property, value, options)
reference.setSync(property, value, options)
  • property [transferable] - The property to set on this object.
  • value [transferable] - The value to set on this object.
  • options [object]
reference.apply(receiver, arguments, options) Promise
reference.applyIgnored(receiver, arguments, options)
reference.applySync(receiver, arguments, options)
reference.applySyncPromise(receiver, arguments, options)
  • receiver [transferable] - The value which will be this.
  • arguments [array] - Array of transferables which will be passed to the function.
  • options [object]
    • timeout [number] - Maximum amount of time in milliseconds this function is allowed to run before execution is canceled. Default is no timeout.
    • arguments [object]
    • result [object]
  • return [transferable]

Will attempt to invoke an object as if it were a function. If the return value is transferable it will be returned to the caller of apply, otherwise it will return an instance of Reference. This behavior can be changed with the result options.

applySyncPromise is a special version of applySync which may only be invoked on functions belonging to the default isolate AND may only be invoked from a non-default thread. Functions invoked in this way may return a promise and the invoking isolate will wait for that promise to resolve before resuming execution. You can use this to implement functions like readFileSync in a way that doesn't block the default isolate. Note that the invoking isolate will not respond to any async functions until this promise is resolved, however synchronous functions will still function correctly. Misuse of this feature may result in deadlocked isolates, though the default isolate will never be at risk of a deadlock.

Class: ExternalCopy [transferable]

Instances of this class represent some value that is stored outside of any v8 isolate. This value can then be quickly copied into any isolate without any extra thread synchronization.

new ivm.ExternalCopy(value, options)
  • value - The value to copy.
  • options [object]
    • transferList [boolean] - An array of ArrayBuffer instances to transfer ownership. This behaves in a similar way to postMessage.
    • transferOut [boolean] - If true this will release ownership of the given resource from this isolate. This operation completes in constant time since it doesn't have to copy an arbitrarily large object. This only applies to ArrayBuffer and TypedArray instances.

Primitive values can be copied exactly as they are. Date objects will be copied as Dates. ArrayBuffers, TypedArrays, and DataViews will be copied in an efficient format. SharedArrayBuffers will simply copy a reference to the existing memory and when copied into another isolate the new SharedArrayBuffer will point to the same underlying data. After passing a SharedArrayBuffer to ExternalCopy for the first time isolated-vm will take over management of the underlying memory block, so a "copied" SharedArrayBuffer can outlive the isolate that created the memory originally.

All other objects will be copied in seralized form using the structured clone algorithm. ExternalCopy can copy objects with deeply nested transferable objects. For example:

let isolate = new ivm.Isolate;
let context = isolate.createContextSync();
let global = context.global;
let data = new ExternalCopy({ isolate, context, global });
ExternalCopy.totalExternalSize [number]

This is a static property which will return the total number of bytes that isolated-vm has allocated outside of v8 due to instances of ExternalCopy.

externalCopy.copy(options)
  • options [object]
    • release [boolean] - If true release() will automatically be called on this instance.
    • transferIn [boolean] - If true this will transfer the resource directly into this isolate, invalidating the ExternalCopy handle.
  • return - JavaScript value of the external copy.

Internalizes the ExternalCopy data into this isolate.

externalCopy.copyInto(options)
  • options [object]
    • release [boolean] - If true release() will automatically be called on this instance.
    • transferIn [boolean] - If true this will transfer the resource directly into this isolate, invalidating the ExternalCopy handle.
  • return [transferable]

Returns an object, which when passed to another isolate will cause that isolate to internalize a copy of this value.

externalCopy.release()

Releases the reference to this copy. If there are other references to this copy elsewhere the copy will still remain in memory, but this handle will no longer be active. Disposing ExternalCopy instances isn't super important, v8 is a lot better at cleaning these up automatically because there's no inter-isolate dependencies.

Shared Options

Many methods in this library accept common options between them. They are documented here instead of being colocated with each instance.

CachedDataOptions
  • cachedData [ExternalCopy[ArrayBuffer]] - This will consume cached compilation data from a previous call to this function. cachedDataRejected will be set to true if the supplied data was rejected by V8.
  • produceCachedData [boolean] - Produce V8 cache data. Similar to the VM.Script option of the same name. If this is true then the returned object will have cachedData set to an ExternalCopy handle. Note that this differs from the VM.Script option slightly in that cachedDataProduced is never set.

Most functions which compile or run code can produce and consume cached data. You can produce cached data and use the data in later invocations to drastically speed up parsing of the same script. You can even save this data to disk and use it in a different process. You can set both cachedData and produceCachedData, in which case new cached data will only be produced if the data supplied was invalid.

NOTE: CachedData contains compiled machine code. That means you should not accept cachedData payloads from a user, otherwise they may be able to run arbitrary code.

ScriptOrigin
  • filename [string] - Filename of this source code
  • columnOffset [number] - Column offset of this source code
  • lineOffset [number] - Line offset of this source code

You may optionally specify information on compiled code's filename. This is used in various debugging contexts within v8, including stack traces and the inspector. It is recommended to use a valid URI scheme, for example: { filename: 'file:///test.js' }, otherwise some devtools may malfunction.

TransferOptions
  • copy [boolean] - Automatically deep copy value
  • externalCopy [boolean] - Automatically wrap value in ExternalCopy instance
  • reference [boolean] - Automatically wrap value in Reference instance
  • promise [boolean] - Automatically proxy any returned promises between isolates. This can be used in combination with the other transfer options.

Any function which moves data between isolates will accept these transfer options. By default only [transferable] values may pass between isolates. Without specifying one of these options the function may ignore the value, throw, or wrap it in a reference depending on the context.

More advanced situations like transferring ownership of ArrayBuffer instances will require direct use of ExternalCopy or Reference.

ThreadCpuProfile

It's a object that contains a thread id and a CpuProfile info.

  • threadId [number] - The thread that isolate runs on.
  • profile [CpuProfile] - The CpuProfile.
CpuProfile

The CpuProfile Object that can be JSON.stringify(cpuProfile), and save to any external file system for later reloaded into chrome dev tool or any other js performance tool to review.

The format should matches the definition in: https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-Profile

  • startTime [number] - The start timestamp when calling .startProfiling.
  • endTime [number] - The end timestamp when calling .stopProfiling,
  • samples [Array] - All sample node id has been collected.
  • timeDeltas [Array] - All the time deltas related to the samples.
  • nodes [Array]
    • hitCount [number]
    • id [id]
    • children [Array]
    • callFrame [CallFrame]
      • functionName [string]
      • url [string] - The filename used in ScriptOrigin
      • scriptId [number]
      • lineNumber [number]
      • columnNumber [number]
      • bailoutReason [string?] - When the JavaScript function bailed out from v8 optimization, this field will present.

EXAMPLES

Below is a sample program which shows basic usage of the library.

// Create a new isolate limited to 128MB
const ivm = require('isolated-vm');
const isolate = new ivm.Isolate({ memoryLimit: 128 });

// Create a new context within this isolate. Each context has its own copy of all the builtin
// Objects. So for instance if one context does Object.prototype.foo = 1 this would not affect any
// other contexts.
const context = isolate.createContextSync();

// Get a Reference{} to the global object within the context.
const jail = context.global;

// This makes the global object available in the context as `global`. We use `derefInto()` here
// because otherwise `global` would actually be a Reference{} object in the new isolate.
jail.setSync('global', jail.derefInto());

// We will create a basic `log` function for the new isolate to use.
jail.setSync('log', function(...args) {
	console.log(...args);
});

// And let's test it out:
context.evalSync('log("hello world")');
// > hello world

// Let's see what happens when we try to blow the isolate's memory
const hostile = isolate.compileScriptSync(`
	const storage = [];
	const twoMegabytes = 1024 * 1024 * 2;
	while (true) {
		const array = new Uint8Array(twoMegabytes);
		for (let ii = 0; ii < twoMegabytes; ii += 4096) {
			array[ii] = 1; // we have to put something in the array to flush to real memory
		}
		storage.push(array);
		log('I\\'ve wasted '+ (storage.length * 2)+ 'MB');
	}
`);

// Using the async version of `run` so that calls to `log` will get to the main node isolate
hostile.run(context).catch(err => console.error(err));
// I've wasted 2MB
// I've wasted 4MB
// ...
// I've wasted 130MB
// I've wasted 132MB
// RangeError: Array buffer allocation failed

FREQUENTLY ASKED QUESTION

There is only 1 frequently asked question:

"How do I pass a [module, function, object, library] into an isolate?"

You don't! Isolates are isolated. An isolate is its own environment with its own heap which is isolated from all other isolates. It may help to think of the question in the context of a web browser. How would you pass a function from nodejs into Firefox? You can't, it is nonsense.

Depending on the function you could just pass the code for the function directly into the isolate and execute it there. That's how a <script /> tag works in our browser metaphor. This works for functions that don't need to do anything such as file access or network requests. Check out Webpack, Rollup, esbuild, etc for bundling solutions.

If you want to perform operations on files, network, native modules, etc then you will need to set up some kind of shim delegate which can perform the operation within nodejs and pass the result back to your isolate. In the browser metaphor this would be like a REST call back to your service.

Finally, and I'm not trying to be mean here, if this explanation doesn't make sense then you really should not be using this module. This is a low-level module which is just one piece of a very complicated problem. If your goal is to run code from untrusted sources then you must have a very comprehensive understanding of JavaScript. You should know where the ECMAScript specification ends and where the HTML, DOM, and other web specifications begin. You should be a security-focused hacker, otherwise you will almost certain make a company-ending mistake. This is not a module for the faint of heart. Turn back now!

ALTERNATIVES

Below is a quick summary of some other options available on nodejs and how they differ from isolated-vm. The table headers are defined as follows:

  • Secure: Obstructs access to unsafe nodejs capabilities
  • Memory Limits: Possible to set memory limits / safe against heap overflow DoS attacks
  • Isolated: Is garbage collection, heap, etc isolated from application
  • Multithreaded: Run code on many threads from a single process
  • Module Support: Is require supported out of the box
  • Inspector Support: Chrome DevTools supported
Module Secure Memory Limits Isolated Multithreaded Module Support Inspector Support
vm
worker_threads
vm2
tiny-worker
isolated-vm

isolated-vm's People

Contributors

alufers avatar artch avatar augjoh avatar davidjb avatar fpedrei avatar gramthanos avatar idleman avatar isker avatar jeromegn avatar jerska avatar johnkmzhou avatar kiddkai avatar koto avatar laverdet avatar marcrock22 avatar mareklibra avatar mwpenny avatar orgads avatar pavlokolodka avatar rs avatar s-diez avatar seanet7or avatar shiftinv avatar spencersharkey avatar vitamindck avatar y-exp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

isolated-vm's Issues

CPU Usage

Would it be possible to get user/system and internal/external CPU usage for code ran into an isolate?

Invoke in VM function with argument AND timeout support

There is two ways to execute a piece of code from the VM:

  • Compile and run a script on the context.
  • Get a reference from a function living in the context, and call apply on it.

With the first solution, we can supply a timeout parameter to the run method in order to limit the execution time. But with this method it is not possible to pass parameters "on the stack". We have to store values in the global context then access them from the script we execute.

With the second method, we can pass (transferable) arguments to the method, like callback etc. But there is no way to pass a timeout to the apply method.

Zero-Copy references between Isolates

I would like to use isolate-vm's multi-threading capabilities to implement parallel sort and scan algorithms, but the copy-in/out requirements add time and space complexity on the order of number of threads.

Are inter-isolate references possible with caveats? isolated-vm enforces reasonable caveats about parallel access to shared resources (i.e.: I/O), might it be possible to implement zero-copy inter-isolate references with caveats (i.e.: read-only, persistent data)?

Assertion failed: (tasks.empty())

I get the following error sometimes when trying to dispose of an Isolate:

Assertion failed: (tasks.empty()), function Dispose, file ../src/shareable_isolate.h, line 357.

I'm only running one context and am waiting for all the code I'm running to be done before disposing of its isolate.

Anything I should be doing before disposing of my Isolate?

Note: I'm using master.

Wire up existing `console` in v8?

v8 has console stuff built in but there's no stdout/err up to it (I think that's all that's missing). Is there a way to hook it up through isolated-vm?

Passing a Reference through an ExternalCopy breaks the reference

Given an object like:

{ functionA: Reference { typeof: 'function' }, numberB: 1 }

Passing set object through ExternalCopy's .copyInto()/.copy() results in the following:

{ functionA: { typeof: 'function' }, numberB: 1 }

Not sure if this is the intended behaviour, but it looks like it tries to serialise the Reference as an object. I'm trying to pass an object containing some functions from the main isolate to another isolate.

Is there a way in which it could keep the original, or .copy() it?

Sample code:

const ivm = require('isolated-vm');

const ref = new ivm.Reference(function aFunction() {});

const objectWithRef = { fn: ref, nr: 1 };

console.dir(ref);
console.dir(new ivm.ExternalCopy(ref).copy());
console.dir(objectWithRef);
console.dir(new ivm.ExternalCopy(objectWithRef).copy());
console.dir(new ivm.Reference(objectWithRef).deref()); // will not work in other isolates

output:

Reference { typeof: 'function' }
{ typeof: 'function' }
{ fn: Reference { typeof: 'function' }, nr: 1 }
{ fn: { typeof: 'function' }, nr: 1 }
{ fn: Reference { typeof: 'function' }, nr: 1 }

How to catch compilation errors with createSnapshot

I can't find a way to catch errors thrown during snapshot creation:

const ivm = require("isolated-vm");

try {
    const snapshot = ivm.Isolate.createSnapshot([{code: '**'}]);
    const isolate = new ivm.Isolate({ snapshot });
} catch (e) {
    console.log('Caught', e);
}

Output:

<isolated-vm>:0: Uncaught SyntaxError: Unexpected token **

Expected output:

Caught SyntaxError: Unexpected token **

CPU/wall time per context?

Would it be possible to get those timings per context? I assume it's a matter of summing the time during which the context "has the lock" on the isolate? (that would probably be wall time though)

TypedArray segfault

I've noticed this when running with an inspector instance:

PID 92654 received SIGSEGV for address: 0x0
0   segfault-handler.node               0x000000010723f168 _ZL16segfault_handleriP9__siginfoPv + 280
1   libsystem_platform.dylib            0x00007fffe68deb3a _sigtramp + 26
2   ???                                 0x4187c18000000000 0x0 + 4721955489821229056
3   node                                0x0000000100540055 _ZN2v88internal12JSTypedArray22MaterializeArrayBufferENS0_6HandleIS1_EE + 601
4   node                                0x000000010066ebee _ZN2v88internal27Runtime_TypedArrayGetBufferEiPPNS0_6ObjectEPNS0_7IsolateE + 70
5   ???                                 0x0000230eefea3c58 0x0 + 38547061619800
6   ???                                 0x0000230eeff7a529 0x0 + 38547062498601
libc++abi.dylib: terminating
[1]    92654 abort      LOG_LEVEL=info ../../superfly/fly/bin/fly server --inspect

Seems to only happen if I take a heap snapshot and then run a lot of requests concurrently (but they're queued one at a time via a mutex.)

Not sure if relevant to anything.

More states getters

The addition of isDisposed helps a lot. Is that also set while it is being disposed of?

It seems like surfacing a few more states would help predicting the lifecycle of isolates and contexts.

It'd be useful to be able to tell when an isolate is being disposed of, maybe isDisposing? I suspect there are other states to the isolate. Is creating an isolate really synchronous?

Contexts also have a few states. While they're being created, while they're being released and when they're actually released.

How to make transferable values from NativeModule?

(Hopefully this is not too much, there's no rush in responding to any of our issues!)

I'm excited by the prospect of the native module work you've done. I think it will help a lot with our project at Fly.

I was wondering what makes a value transferable exactly? I'd be interested in making constructs that can be used both in the node isolate and plain v8 isolates.

For instance, a ReadableStream implementation would allow reading from a source natively, both in nodejs and v8 isolates. If the underlying stream is managed outside of both node and v8, then I imagine it should be usable by either isolate. Of course it would be locked to a single reader at a time (and won't be readable after the first read, like the spec prescribes.)

Our idea is to allow for passing ReadableStream created in either nodejs or v8, back and forth. Reading from it would not involve calling back into the other isolate.

Is this something in the realm of possibilities?

Used somewhere in production?

This is a very interesting project. Is this used somewhere in production or it is still at the experimention stage?

Question: How can I import functions? All i keep getting is references...

const go = ($ = {}) => {
    const vm = $.vm || newVM({ memoryLimit: 8 });
    const globals = $.globals || {};
    const ctx = $.context || newContext(vm);
    const gl = globalSetter(ctx);
    Object.keys(globals).forEach(name => {
        const inp = globals[name];
        gl.setSync(`__${name}`, new ivm.Reference(inp));
    });
    // v This one was taken from example v
    gl.setSync("_log", new ivm.Reference(function (...$) {
        console.log(...$);
    }));
    // ^^^^^^^^^^
    let script = $.code || (() => { });
    vm.compileScriptSync(`new ` + script).runSync(ctx);
};

Not even the exampe code worked, it still comes up as a reference.

Wait for events

Is there a way to get a promise that would resolve when all events scheduled by the context are done?

Event loop snafu

While integrating isolated-vm within a network service, I noticed some strange behavior with async methods like createContext. When testing alone, everything works, but once integrated, those promises never trigger.

I think there is something fishy with the way those methods interact with event loop. For instance, in the example below, if I add something to the event loop with setTimeout, the createContext will return after the timeout. Without the timeout, the promise resolves immediately:

const ivm = require("isolated-vm");

const i = new ivm.Isolate();
i.createContext().then(() => {
    console.log('got context');
});

setTimeout(() => {
    console.log('timeout');
}, 10000);

Result:

<10 seconds>
timeout
got context

Single entrypoint

This is probably caused by some misunderstanding from my side, but I'd like to know how I'd go about providing a single entry point (function) to pass data into my isolate. Since the isolate contains user code, putting data into the global context or declaring a top level named function is not really an option, because the user will be able to call and/or modify the data.

Therefore I've tried the following, wrapped in an IIFE because top level anonymous functions are not allowed.

    const isolate = new ivm.Isolate()
    const context = await isolate.createContext()

    const script = await isolate.compileScript(`
        ;(function() {
            return function() {}
        }())      
    `)

    return await script.run(context)

Unfortunately this returns undefined, since it works with primitives (numbers, strings, null), but not with functions or objects, I assume it has something to do with the value not being transferable.

Could you please explain if I'm missing something here or provide some alternative solution?

Let me know if you need any additonal information regarding my use case.

Access node's I/O from any Isolate

Hi!
I was wandering if there is any possibility to access the file system or make network calls from an Isolate without a stub in the main isolate.

My goal here is to possibly make a require-like system that can be used inside any Isolate and load code via it. I tried to share the require function of the main isolate, but the only way i could do it was through a stub that requires the module and then passes it as a reference, but in my understanding, that will still execute on the main isolate.

Any guidance will be appreciated 😃

ivm somehow changes between context

Hey Marcel,

I found this weird behaviour where contexts will have a different ivm (initially set globally) in subsequent runs.

I'm not sure how to explain this better, I have a test case:

const ivm = require('isolated-vm');

const iso = new ivm.Isolate()
const script = `function test(arg1){
  return arg1 instanceof ivm.Reference
}`

async function makeContext() {
  const ctx = await iso.createContext()

  const g = await ctx.globalReference()
  await g.set("ivm", ivm)

  const s = await iso.compileScript(script)
  await s.run(ctx)

  const fn = await g.get("test")
  console.log(await fn.apply(null, [new ivm.Reference(function () { })]))
}

makeContext()
makeContext()

This outputs:

true
false

Am I doing something wrong? I would expect the returned value to be true all the time.

minor errors installing fresh clone

This is a great project, looking forward to trying out. When running npm run install on a fresh clone I got two errors in compilation:

../src/isolate/../external_copy.h:123:37: error: no member named 'free' in namespace 'std'; did you mean simply 'free'?

and

../src/isolate/class_handle.h:23:27: error: unknown type name 'nullptr_t'; did you mean 'std::nullptr_t'

After making these two changes it compiled great and npm test passes the test suite:

  • adding #include <stdlib> to external_copy.h.
  • changing nullptr_t to std::nullptr_t in class_handle.h:23:27

It might be possible that these be just compiler version issues instead. I tried clang and gcc-7.

Best way to pass object with Reference

What's the best way to pass objects to isolate that contain Reference.

const ivm = require('isolated-vm');
const isolate = new ivm.Isolate({ memoryLimit: 128 });

const context = isolate.createContextSync();
const globalReference = context.globalReference();

const obj = {
  a: 1,
  b: 2,
  c: new ivm.Reference(function() {})
}

globalReference.setSync('obj', new ivm.ExternalCopy(obj).copyInto());

let r = isolate.compileScriptSync('obj.a + obj.b').runSync(context);
console.log(r);

This gives an error:

#<Reference> could not be cloned.

As I understand Reference can't be passed to ExternalCopy. What's the best way to transfer objects that contain functions?

transfer: true used on copy() or copyInto() does not work as documented

According to the readme, it's possible to use {transfer: true} during .copy() and .copyInto(), but with a simple test case I was unable to make this pass.

const ivm = require('isolated-vm')
const iso = new ivm.Isolate()
const toArrayBuffer = require('to-arraybuffer')

const ctx = iso.createContextSync()

let arrBuf = toArrayBuffer(Buffer.from("string"))
console.log("before", Buffer.from(arrBuf))

let g = ctx.globalReference()
g.setSync("blah", new ivm.ExternalCopy(arrBuf).copyInto({ transfer: true }))

console.log("after", Buffer.from(arrBuf))

This outputs:

$ node test.js
before <Buffer 73 74 72 69 6e 67>
after <Buffer 73 74 72 69 6e 67>

If I move the transfer option to the constructor call, it does work:

$ node test.js
before <Buffer 73 74 72 69 6e 67>
after <Buffer >

Maybe I misunderstood the documentation though.

Catch async uncaught exceptions

Would it be possible to catch async uncaught exception on a per context basis?

const ivm = require('isolated-vm');
const isolate = new ivm.Isolate();
const context = isolate.createContextSync();
const jail = context.globalReference();

jail.setSync('asyncException', new ivm.Reference(() => {
    setTimeout(() => {throw new Error()}, 1000);
}));

const script = isolate.compileScriptSync('asyncException.apply()');

script.run(context).catch((e) => {
    // Won't catch async exception, would be nice to be able to wait for
    // all scheduled events and capture their exceptions if any.
});

Execution fails with debugger attached

NodeJS: 8.9.4
isolate-vm: 1.0.0

Every time I try to execute script with attached debugger, I get the following:

/usr/bin/node[743]: ../src/node.cc:1456:void node::InternalCallbackScope::Close(): Assertion `(env_->execution_async_id()) == (0)' failed.
 1: node::Abort() [/usr/bin/node]
 2: 0x121a6bb [/usr/bin/node]
 3: 0x121eaf6 [/usr/bin/node]
 4: node::NodePlatform::FlushForegroundTasksInternal() [/usr/bin/node]
 5: node::inspector::NodeInspectorClient::runMessageLoopOnPause(int) [/usr/bin/node]
 6: v8_inspector::V8Debugger::handleProgramBreak(v8::Local<v8::Context>, v8::Local<v8::Object>, v8::Local<v8::Value>, v8::Local<v8::Array>, bool, bool) [/usr/bin/node]
 7: v8_inspector::V8Debugger::BreakProgramRequested(v8::Local<v8::Context>, v8::Local<v8::Object>, v8::Local<v8::Value>) [/usr/bin/node]
 8: v8::internal::Debug::OnDebugBreak(v8::internal::Handle<v8::internal::Object>) [/usr/bin/node]
 9: v8::internal::Debug::Break(v8::internal::JavaScriptFrame*) [/usr/bin/node]
10: v8::internal::Runtime_DebugBreakOnBytecode(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/bin/node]
11: 0x104ca98463d

Here is my code:

async function executeScript(type, data, code) {
    console.time('Exec');

    let result = null;
    let error = null;

    const compiledScript = compiledScripts.get(type);

    if (compiledScript) {
        let globalContext = await isolate.createContext({ inspector: true });
        let globalRef = globalContext.globalReference();

        await globalRef.set('__code', code);
        await globalRef.set('__data', JSON.stringify(data));

        try {
            result = await compiledScript.run(globalContext, runOptions);
        } catch (e) {
            error = e;
        }

        // Reset values
        await globalRef.set('__code', null);
        await globalRef.set('__data', null);

        globalRef.dispose();
        globalContext.release();

        globalRef = null;
        globalContext = null;
    }

    console.timeEnd('Exec');

    if (error) {
        return Promise.reject(error);
    }

    return Promise.resolve(result);
}

Transferable values and errors

Is it possible to know beforehand if a value is transferable or not?

It appears:

  • string, number, boolean types are transferable as-is because they're primitives
  • the return value of ExternalCopy(value).copyInto() is transferable where value is anything

Is it possible to detect when a value is transferable? I can check for primitives, but I'm not sure how to tell from a copied ExternalCopy. The TypeScript definition says it returns the assigned type T, but I'm not sure it can be trusted. Even if it did, how would I know it's already been set as transferable?

I've started getting promise rejections when using apply on an ivm.Reference<Function>. I'm wondering if the return value from those functions are passed in. I switched everything to return undefined, but I still get the

A non-transferable value was passed

error if I check .caught() on that promise.

Is it possible this is coming from a different .apply() call? Given this package is 100% C++, it is hard to debug, figure out return values and the general flow of things.

Releases and versioning

Would it be possible to make releases more frequently? I tend to have to use the github source in my npm package to benefit from the latest features.

What versioning scheme does isolated-vm adhere to?

There were a lot of features added lately, but I've only seen patch version bumps.

I find changelogs painstaking to maintain, we've started using the standard-version npm package for this. As long as the commits follow the Conventional Commits format, it'll automatically decide how to bump the version.

Script run (async) does not reject the promise on error

The timeout on Script.run seems to interrupt the execution but does not reject the returned promise.

Same with exceptions thrown in unchained promises:

const ivm = require("isolated-vm");

function runSync() {
	const isolate = new ivm.Isolate();
	const context = isolate.createContextSync();
	const script = isolate.compileScriptSync('new Promise(() => {throw new Error("some error")})');

	try {
		script.runSync(context);
		console.log('sync not caught');
	} catch (e) {
		console.log('sync caught', e);
	}
}



async function runAsync() {
	const isolate = new ivm.Isolate();
	const context = await isolate.createContext();
	const script = await isolate.compileScript('new Promise(() => {throw new Error("some error")})');

	try {
		await script.run(context);
		console.log('async not caught');
	} catch (e) {
		console.log('async caught', e);
	}
}

runSync();
runAsync();

Output:

sync caught Error: some error
    at runSync (…:9:10)
    at Object.<anonymous> (…:31:1)
    at Module._compile (module.js:624:30)
    at Object.Module._extensions..js (module.js:635:10)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)
    at Function.Module.runMain (module.js:665:10)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3
async not caught

Inspector question with multiple contexts

The inspector has been working great, but I'm running into issues where breakpoints don't work anymore (either manually set in the inspector or via the debugger keyword) when I'm using a new context.

Our development server will reload the code and create a new context for each request. The context and isolate still both have the {inspector: true} option on them.

The script has a different filename too (I'm appending the sha1 of the source to the filename.)

Any idea why the inspector would stop working in these cases?

Assertion failed: (try_catch.HasCaught())

Another assertion failure.

Assertion failed: (try_catch.HasCaught()), function operator(), file ../src/shareable_isolate.h, line 776.

I was able to get there running this code:

const ivm = require('isolated-vm')

const iso = new ivm.Isolate();

(async function () {
  let i = 0
  while (true) {
    console.log("iteration", i)
    await iso.createContext()
    i++
  }
})()

Wouldn't it be better to not use those assertions? They appear to crash the process entirely. Couldn't they just throw some errors that could be caught in node?

GlobalObject leaky?

Probably our fault, but I was wondering if the global object might leak memory even after the context has been released.

We've been having ever-growing heap on isolates. I've used the inspector to record an allocation timeline and noticed the global object seems to appear very often.

image

image

image

I've been careful not to keep references of anything from the context and releasing it after each use.

That's in development which goes like this:

  • An isolate is created with 128 mb memory limit
  • A context is created on each http request, it's bootstrapped and then we run the user's script
  • We dispatch the fetch event in the context
  • After all is done with the request (FetchEvent has responded) we "finalize" the context (wait on all timers and all "callbacks" that are supposed to call back into the isolate) and then we release the context.

All references to the timers and callbacks are removed.

Any pointers on how to keep the isolate lean after each request? I suspect some of our bootstrapped code is not memory efficient, but this seems kind of intense.

That allocation timeline was made on a very small app, all it does it respond to a callback with a default Response with some text. That heap grew that much after only about 30 requests. Basically each time we keep the isolate around and create a new context, it adds 1.6MB onto the heap of the isolate and it's not obvious how to get rid of it.

Handling unhandled promise rejections

Is an event like browsers' unhandledrejection or node's unhandledRejection possible with V8?

It looks like the docs for RunMicroTasks says any errors are swallowed. I can't quite tell how browsers or node.js do it though.

Assertion failed for null pointer

From time to time, I get

node: ../src/platform_delegate.h:45: virtual void ivm::PlatformDelegate::CallOnForegroundThread(v8::Isolate*, v8::Task*): Assertion `s_isolate != nullptr' failed.

While running our specs on travis. I have not seen that locally. It's not happening consistently on travis either (we have some succeeding builds with the same code.)

It seems to happen at the same spot every time. Which is strange. It's not a particularly special scenario.

I'm sure this is due to some weird logic in our code, like a race condition, but I would expect isolated-vm to fail in a catchable way. Now it just crashes the process.

You can see relevant logs here: https://travis-ci.org/superfly/fly/builds/333482078 you can run the https://github.com/superfly/fly specs with npm test if you want to try and reproduce it.

Is it possible to share node modules?

Hi,

I just read the documentation but all examples compile very simple code which has no state and no link to the host state.

I'm wondering: is it possible to share node modules like puppeteer for instance? If you don't provide access to the fs module for instance, and the library you provided need it, what happens?

In my case, I have a pool of connections to Chrome and I want to inject it in a isolated script. By reading the documentation I'm not very confident that's possible. It seems to me everything shared with an isolate is serialized and thus it only applies to raw data but not stateful objects.

I would love a bit of clarification. Thanks.

New crash with latest 1.5.1

I think this is new, here's 2 core dumps:

[Current thread is 1 (Thread 0x7fb07c846700 (LWP 38))]
(gdb) where
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007fb0986b242a in __GI_abort () at abort.c:89
#2  0x00007fb0991e60ad in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007fb0991e4066 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007fb0991e40b1 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007fb0991e42c9 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007fb09920cf3e in std::__throw_system_error(int) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7  0x00007fb09920f0e8 in std::thread::join() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#8  0x00007fb096417b0c in thread_pool_t::resize (size=0, this=0x7fb096679640 <ivm::IsolateEnvironment::Scheduler::thread_pool>) at ../src/isolate/../thread_pool.h:130
#9  thread_pool_t::~thread_pool_t (this=0x7fb096679640 <ivm::IsolateEnvironment::Scheduler::thread_pool>, __in_chrg=<optimized out>) at ../src/isolate/../thread_pool.h:64
#10 0x00007fb0986b3940 in __run_exit_handlers (status=-1, listp=0x7fb098a175d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:83
#11 0x00007fb0986b399a in __GI_exit (status=<optimized out>) at exit.c:105
#12 0x00007fb084368a7b in segfault_handler(int, siginfo_t*, void*) () from /usr/src/app/node_modules/segfault-handler/build/Release/segfault-handler.node
#13 <signal handler called>
#14 __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:286
#15 0x00005619306feb57 in v8::internal::JSTypedArray::MaterializeArrayBuffer(v8::internal::Handle<v8::internal::JSTypedArray>) ()
#16 0x00005619306fee80 in v8::internal::JSTypedArray::GetBuffer() ()
#17 0x00005619308ca73f in v8::internal::Runtime_TypedArrayGetBuffer(int, v8::internal::Object**, v8::internal::Isolate*) ()

... + around 120 lines of ??

and

[Current thread is 1 (Thread 0x7fb82e99eb80 (LWP 13))]
(gdb) where
#0  0x00007fb82d559317 in kill () at ../sysdeps/unix/syscall-template.S:84
#1  0x0000558c28bef669 in uv_kill (pid=<optimized out>, signum=<optimized out>) at ../deps/uv/src/unix/process.c:579
#2  0x0000558c289ab154 in node::Kill(v8::FunctionCallbackInfo<v8::Value> const&) ()
#3  0x0000558c281d92a8 in v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) ()
#4  0x0000558c282491ff in v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) ()
#5  0x0000558c28249a89 in v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) ()

... + around 40 lines of ??

Async operations are clogging the loop?

I've been using isolated-vm to process a stream with custom JS functions which I run within a V8 instance, but I'm encountering sluggishness in places.

It goes something like:

  • get a Readable stream
  • create a Transform stream which passes the input to a function running within the isolated vm
  • wait for callback to be called with desired output
  • push into the stream
  • call the Transform callback
const tr = new Transform({
  transform(chunk, encoding, callback) {
    someIsolatedFunction.apply(null, [chunk.toString(), new ivm.Reference(function(err, newChunk){
      // error checking
      tr.push(newChunk)
      callback()
    }]
})

src.pipe(tr).pipe(dst)

There is major slowness if I don't use a setTimeout(fn, 0) or setImmediate(fn) for the callback().

This is only an example, I've noticed that in many places.

I run everything using the async functions provided by isolated-vm. Everything works great except for slowness that can reach up to 1-5 seconds (during which nothing is happening.)

I'm sure I'm missing something or doing something incorrectly. If this is normal and I should pepper my code with setImmediate, that's ok with me, but I'm not sure where to put them.

applySyncPromise segfault

Hey there,

We're loving the new applySyncPromise function and plan on using it to make our isolates leaner and more robust.

In my tests, under high concurrency conditions, it seems like I inevitably end up with a segfault like:

PID 16308 received SIGSEGV for address: 0x0
0   segfault-handler.node               0x0000000107178168 _ZL16segfault_handleriP9__siginfoPv + 280
1   libsystem_platform.dylib            0x00007fffe68deb3a _sigtramp + 26
2   node                                0x000000010043326b _ZNK2v88internal23StackTraceFrameIterator12IsValidFrameEPNS0_10StackFrameE + 127
3   isolated_vm.node                    0x0000000103f1b3f8 _ZN3ivm14ThreePhaseTask7RunSyncERNS_13IsolateHolderEb + 296
4   isolated_vm.node                    0x0000000103f4be5f _ZN3ivm14ThreePhaseTask3RunILi4ENS_11ApplyRunnerEJRNS_15ReferenceHandleERN2v810MaybeLocalINS5_5ValueEEERNS6_INS5_5ArrayEEERNS6_INS5_6ObjectEEERNSt3__110shared_ptrINS_12RemoteHandleINS5_7ContextEEEEERNSH_INSI_IS7_EEEEEEENS5_5LocalIS7_EERNS_13IsolateHolderEDpOT1_ + 175
5   isolated_vm.node                    0x0000000103f4bf57 _ZN3ivm14FunctorRunners11RunCallbackIZNS_11ClassHandle17ParameterizeEntryILin1EPFN2v85LocalINS4_5ValueEEEPNS_15ReferenceHandleENS4_10MaybeLocalIS6_EENSA_INS4_5ArrayEEENSA_INS4_6ObjectEEEEXadL_ZNS2_10MethodCastIMS8_FS7_SB_SD_SF_EE6InvokeIXadL_ZNS8_5ApplyILi4EEES7_SB_SD_SF_EEEES7_S9_SB_SD_SF_EEEEvRKNS4_20FunctionCallbackInfoIS6_EEEUlvE_SQ_EEvRT0_T_ + 135
6   node                                0x00000001001cf62a _ZN2v88internal25FunctionCallbackArguments4CallEPFvRKNS_20FunctionCallbackInfoINS_5ValueEEEE + 430
7   node                                0x0000000100216030 _ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE + 775
8   node                                0x000000010021574a _ZN2v88internalL26Builtin_Impl_HandleApiCallENS0_16BuiltinArgumentsEPNS0_7IsolateE + 259
9   ???                                 0x000002d21b7842fd 0x0 + 3101427254013
libc++abi.dylib: terminating

Not entirely sure what triggers that.

Sometimes I also get the following with no stacktrace:

libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument

Running node 8.10 on macos.

I'm not sure if this happens because something is being released too early? I can't reproduce in a simpler test case releasing right after running my function in an infinite loop.

Segfault v8::SnapshotCreator::CreateBlob

Got a segfault with the following code (see stack trace below):

"use strict";

const ivm = require("isolated-vm");
class Function {
    constructor(code, memoryLimit) {
        const snapshot = ivm.Isolate.createSnapshot([{
                code,
                filename: "handler.js",
            }]);
        this.isolate = new ivm.Isolate({ memoryLimit, snapshot });
    }
    async execute(request, context, callback) {
        try {
            const ctx = await this.isolate.createContext();
            const jail = ctx.globalReference();
            jail.set('$callback', new ivm.Reference((result) => {
                callback(result);
            }));
            const handler = await jail.get('handler');
            const $request = new ivm.ExternalCopy(request).copyInto();
            const $context = new ivm.ExternalCopy(context).copyInto();
            const $callback = new ivm.ExternalCopy((result) => {
                $callback.apply(undefined, [new ivm.ExternalCopy(result).copyInto()]);
            }).copyInto();
            await handler.apply(undefined, [$request, $context, $callback]);
        }
        catch (e) {
            console.error(e);
        }
    }
}

const h = '' + async function handler(request, _, callback) {
    callback({ status: 200, body: 'hello world' });
};
const f = new Function(h);
f.execute({ uri: '/test' }, null, (result) => {
    console.log(result.body);
});
Process:               node [99101]
Path:                  /usr/local/Cellar/node/8.7.0/bin/node
Identifier:            node
Version:               ???
Code Type:             X86-64 (Native)
Parent Process:        zsh [90637]
Responsible:           node [99101]
User ID:               501

Date/Time:             2017-10-22 05:25:39.802 -0700
OS Version:            Mac OS X 10.13 (17A405)
Report Version:        12
Bridge OS Version:     3.0 (14Y661)
Anonymous UUID:        8639B6BE-9E0D-4033-485E-CFA43926FE12

Sleep/Wake UUID:       EA1F9B1B-3999-454B-9BBE-AC8AD38C74FB

Time Awake Since Boot: 290000 seconds
Time Since Wake:       19000 seconds

System Integrity Protection: enabled

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Segmentation fault: 11
Termination Reason:    Namespace SIGNAL, Code 0xb
Terminating Process:   exc handler [0]

VM Regions Near 0:
--> 
    __TEXT                 0000000100000000-0000000100e32000 [ 14.2M] r-x/rwx SM=COW  /usr/local/Cellar/node/8.7.0/bin/node

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   node                          	0x00000001001a928d v8::SnapshotCreator::CreateBlob(v8::SnapshotCreator::FunctionCodeHandling) + 671
1   isolated_vm.node              	0x0000000103e8a302 ivm::CreateSnapshot(v8::Local<v8::Array>, v8::MaybeLocal<v8::String>) + 1618
2   isolated_vm.node              	0x0000000103eb504b void ivm::ClassHandle::ParameterizeEntry<0, v8::Local<v8::Value> (*)(v8::Local<v8::Array>, v8::MaybeLocal<v8::String>), &(ivm::CreateSnapshot(v8::Local<v8::Array>, v8::MaybeLocal<v8::String>))>(v8::FunctionCallbackInfo<v8::Value> const&) + 123
3   node                          	0x00000001001d08a4 v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) + 430
4   node                          	0x0000000100219e8b v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) + 775
5   node                          	0x00000001002195ae v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) + 259
6   ???                           	0x00000448c268463d 0 + 4710545770045
7   ???                           	0x00000448c27745fc 0 + 4710546753020
8   ???                           	0x00000448c273f9ce 0 + 4710546536910
9   ???                           	0x00000448c2685d1b 0 + 4710545775899
10  ???                           	0x00000448c268573e 0 + 4710545774398
11  ???                           	0x00000448c2775d87 0 + 4710546759047
12  ???                           	0x00000448c273f9ce 0 + 4710546536910
13  ???                           	0x00000448c2774048 0 + 4710546751560
14  ???                           	0x00000448c273f9ce 0 + 4710546536910
15  ???                           	0x00000448c277474a 0 + 4710546753354
16  ???                           	0x00000448c273f9ce 0 + 4710546536910
17  ???                           	0x00000448c277474a 0 + 4710546753354
18  ???                           	0x00000448c273f9ce 0 + 4710546536910
19  ???                           	0x00000448c27745fc 0 + 4710546753020
20  ???                           	0x00000448c273f9ce 0 + 4710546536910
21  ???                           	0x00000448c2775002 0 + 4710546755586
22  ???                           	0x00000448c273f9ce 0 + 4710546536910
23  ???                           	0x00000448c2774048 0 + 4710546751560
24  ???                           	0x00000448c273f9ce 0 + 4710546536910
25  ???                           	0x00000448c2792229 0 + 4710546874921
26  ???                           	0x00000448c273f9ce 0 + 4710546536910
27  ???                           	0x00000448c2774da1 0 + 4710546754977
28  ???                           	0x00000448c273f9ce 0 + 4710546536910
29  ???                           	0x00000448c273e479 0 + 4710546531449
30  ???                           	0x00000448c268410d 0 + 4710545768717
31  node                          	0x00000001004294af v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, bool, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, v8::internal::Handle<v8::internal::Object>, v8::internal::Execution::MessageHandling) + 613
32  node                          	0x00000001004291eb v8::internal::(anonymous namespace)::CallInternal(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*, v8::internal::Execution::MessageHandling) + 160
33  node                          	0x00000001001bcf57 v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 307
34  node                          	0x0000000100760e4e node::LoadEnvironment(node::Environment*) + 575
35  node                          	0x0000000100768b07 node::Start(v8::Isolate*, node::IsolateData*, int, char const* const*, int, char const* const*) + 433
36  node                          	0x0000000100762991 node::Start(uv_loop_s*, int, char const* const*, int, char const* const*) + 366
37  node                          	0x0000000100762603 node::Start(int, char**) + 474
38  node                          	0x0000000100002534 start + 52

Thread 1:
0   libsystem_kernel.dylib        	0x00007fff78e39e7e __psynch_cvwait + 10
1   libsystem_pthread.dylib       	0x00007fff78f75662 _pthread_cond_wait + 732
2   node                          	0x0000000100831fef uv_cond_wait + 9
3   node                          	0x00000001007a1991 node::TaskQueue<v8::Task>::BlockingPop() + 55
4   node                          	0x00000001007a13d1 node::BackgroundRunner(void*) + 46
5   libsystem_pthread.dylib       	0x00007fff78f746c1 _pthread_body + 340
6   libsystem_pthread.dylib       	0x00007fff78f7456d _pthread_start + 377
7   libsystem_pthread.dylib       	0x00007fff78f73c5d thread_start + 13

Thread 2:
0   libsystem_kernel.dylib        	0x00007fff78e39e7e __psynch_cvwait + 10
1   libsystem_pthread.dylib       	0x00007fff78f75662 _pthread_cond_wait + 732
2   node                          	0x0000000100831fef uv_cond_wait + 9
3   node                          	0x00000001007a1991 node::TaskQueue<v8::Task>::BlockingPop() + 55
4   node                          	0x00000001007a13d1 node::BackgroundRunner(void*) + 46
5   libsystem_pthread.dylib       	0x00007fff78f746c1 _pthread_body + 340
6   libsystem_pthread.dylib       	0x00007fff78f7456d _pthread_start + 377
7   libsystem_pthread.dylib       	0x00007fff78f73c5d thread_start + 13

Thread 3:
0   libsystem_kernel.dylib        	0x00007fff78e39e7e __psynch_cvwait + 10
1   libsystem_pthread.dylib       	0x00007fff78f75662 _pthread_cond_wait + 732
2   node                          	0x0000000100831fef uv_cond_wait + 9
3   node                          	0x00000001007a1991 node::TaskQueue<v8::Task>::BlockingPop() + 55
4   node                          	0x00000001007a13d1 node::BackgroundRunner(void*) + 46
5   libsystem_pthread.dylib       	0x00007fff78f746c1 _pthread_body + 340
6   libsystem_pthread.dylib       	0x00007fff78f7456d _pthread_start + 377
7   libsystem_pthread.dylib       	0x00007fff78f73c5d thread_start + 13

Thread 4:
0   libsystem_kernel.dylib        	0x00007fff78e39e7e __psynch_cvwait + 10
1   libsystem_pthread.dylib       	0x00007fff78f75662 _pthread_cond_wait + 732
2   node                          	0x0000000100831fef uv_cond_wait + 9
3   node                          	0x00000001007a1991 node::TaskQueue<v8::Task>::BlockingPop() + 55
4   node                          	0x00000001007a13d1 node::BackgroundRunner(void*) + 46
5   libsystem_pthread.dylib       	0x00007fff78f746c1 _pthread_body + 340
6   libsystem_pthread.dylib       	0x00007fff78f7456d _pthread_start + 377
7   libsystem_pthread.dylib       	0x00007fff78f73c5d thread_start + 13

Thread 5:
0   libsystem_kernel.dylib        	0x00007fff78e30eb2 semaphore_wait_trap + 10
1   node                          	0x0000000100831ec4 uv_sem_wait + 16
2   node                          	0x00000001007d50b4 node::inspector::(anonymous namespace)::StartIoThreadMain(void*) + 21
3   libsystem_pthread.dylib       	0x00007fff78f746c1 _pthread_body + 340
4   libsystem_pthread.dylib       	0x00007fff78f7456d _pthread_start + 377
5   libsystem_pthread.dylib       	0x00007fff78f73c5d thread_start + 13

Thread 6:
0   libsystem_kernel.dylib        	0x00007fff78e3a1d2 __semwait_signal + 10
1   libsystem_c.dylib             	0x00007fff78db5774 nanosleep + 199
2   libc++.1.dylib                	0x00007fff76d6a934 std::__1::this_thread::sleep_for(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l> > const&) + 73
3   isolated_vm.node              	0x0000000103eb5f53 ivm::PlatformDelegate::CallDelayedOnForegroundThread(v8::Isolate*, v8::Task*, double)::'lambda'()::operator()() const + 99
4   isolated_vm.node              	0x0000000103eb5e7d void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, ivm::PlatformDelegate::CallDelayedOnForegroundThread(v8::Isolate*, v8::Task*, double)::'lambda'()> >(void*) + 45
5   libsystem_pthread.dylib       	0x00007fff78f746c1 _pthread_body + 340
6   libsystem_pthread.dylib       	0x00007fff78f7456d _pthread_start + 377
7   libsystem_pthread.dylib       	0x00007fff78f73c5d thread_start + 13

Thread 0 crashed with X86 Thread State (64-bit):
  rax: 0x0000000000000000  rbx: 0x0000000104804200  rcx: 0x0000000000000001  rdx: 0x0000000000000000
  rdi: 0x0000000000200000  rsi: 0x0000000000400000  rbp: 0x00007ffeefbfd640  rsp: 0x00007ffeefbfd250
   r8: 0x0000000000000000   r9: 0x0000000000000000  r10: 0x00000000103806d5  r11: 0x0000000000000001
  r12: 0x0000000000000001  r13: 0x0000000000000001  r14: 0x0000000104876208  r15: 0x0000000103a07af0
  rip: 0x00000001001a928d  rfl: 0x0000000000010202  cr2: 0x000000010066f313
  
Logical CPU:     0
Error Code:      0x0200014e
Trap Number:     133


Binary Images:
       0x100000000 -        0x100e31bdf +node (???) <3710F1B7-0B39-3D3C-A6BB-926C35289B97> /usr/local/bin/node
       0x101a73000 -        0x101bd0fff +libicui18n.59.dylib (0) <254E3D9F-1737-34F2-9139-2EC3C20D57AE> /usr/local/opt/icu4c/lib/libicui18n.59.dylib
       0x101caf000 -        0x101db1ff3 +libicuuc.59.dylib (0) <E77E3BDF-AA71-3B39-A645-4772F7BA24E9> /usr/local/opt/icu4c/lib/libicuuc.59.dylib
       0x101e1e000 -        0x103731fff +libicudata.59.1.dylib (0) <9423069A-AEDD-339F-90CD-23A846D72CB9> /usr/local/opt/icu4c/lib/libicudata.59.1.dylib
       0x103e87000 -        0x103ed8ff7 +isolated_vm.node (???) <47410F47-8CCA-3588-A8C4-CA88B3B0D81E> /tmp/*/isolated_vm.node
       0x10503c000 -        0x10508698f  dyld (519.2.1) <002B0442-3D59-3159-BA10-1C0A77859C6A> /usr/lib/dyld
    0x7fff516fe000 -     0x7fff51b9dfff  com.apple.CoreFoundation (6.9 - 1443.13) <2881430B-73E5-32C1-B62D-7CEB68A616F5> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
    0x7fff765e6000 -     0x7fff76619fff  libclosured.dylib (519.2.1) <31A6AC03-8F51-367E-9E00-FF0F1AD10F6F> /usr/lib/closure/libclosured.dylib
    0x7fff7678f000 -     0x7fff76790ff3  libDiagnosticMessagesClient.dylib (104) <9712E980-76EE-3A89-AEA6-DF4BAF5C0574> /usr/lib/libDiagnosticMessagesClient.dylib
    0x7fff76af7000 -     0x7fff76af8ff3  libSystem.B.dylib (1252) <BBBE6ABC-FD84-30A3-B699-95F1D6EFB334> /usr/lib/libSystem.B.dylib
    0x7fff76d23000 -     0x7fff76d79fff  libc++.1.dylib (400.9) <FCF5E1F6-2B04-3545-8004-F3AB32FED172> /usr/lib/libc++.1.dylib
    0x7fff76d7a000 -     0x7fff76d9eff7  libc++abi.dylib (400.7) <217656D5-BC40-37FF-B322-91CB2AAD4F34> /usr/lib/libc++abi.dylib
    0x7fff77845000 -     0x7fff77a6bffb  libicucore.A.dylib (59117.0.1) <EC29683E-E9DE-3100-8F75-D1FEBB82AAB2> /usr/lib/libicucore.A.dylib
    0x7fff780e7000 -     0x7fff784d57e7  libobjc.A.dylib (723) <93A92316-DE1E-378C-8891-99720B50D075> /usr/lib/libobjc.A.dylib
    0x7fff78ad4000 -     0x7fff78ae6ffb  libz.1.dylib (70) <48C67CFC-940D-3857-8DAD-857774605352> /usr/lib/libz.1.dylib
    0x7fff78b83000 -     0x7fff78b87ff7  libcache.dylib (80) <354F3B7D-404E-3398-9EBF-65CA2CE65211> /usr/lib/system/libcache.dylib
    0x7fff78b88000 -     0x7fff78b92ff3  libcommonCrypto.dylib (60118.1.1) <6C502A55-3B54-3B48-BA7B-FA39F56C0B1E> /usr/lib/system/libcommonCrypto.dylib
    0x7fff78b93000 -     0x7fff78b9afff  libcompiler_rt.dylib (62) <4487CFBA-A5D7-3282-9E6B-94CAD7BE507E> /usr/lib/system/libcompiler_rt.dylib
    0x7fff78b9b000 -     0x7fff78ba3fff  libcopyfile.dylib (146) <7E4BD264-5617-339B-AC73-E8D07EF2C51D> /usr/lib/system/libcopyfile.dylib
    0x7fff78ba4000 -     0x7fff78c28ff7  libcorecrypto.dylib (562) <7974762C-DEF7-3056-9856-9821C27846DB> /usr/lib/system/libcorecrypto.dylib
    0x7fff78caf000 -     0x7fff78ce8ff7  libdispatch.dylib (913.1.6) <0DD78497-6A2A-350A-99EF-15BF41EA07DD> /usr/lib/system/libdispatch.dylib
    0x7fff78ce9000 -     0x7fff78d06ff7  libdyld.dylib (519.2.1) <2597D818-42D2-3375-BD9D-451D5942A6BA> /usr/lib/system/libdyld.dylib
    0x7fff78d07000 -     0x7fff78d07ffb  libkeymgr.dylib (28) <6D84A96F-C65B-38EC-BDB5-21FD2C97E7B2> /usr/lib/system/libkeymgr.dylib
    0x7fff78d15000 -     0x7fff78d15ff7  liblaunch.dylib (1205.1.10) <5AD77A68-BB4D-33AA-AA4F-DF51D0972FD9> /usr/lib/system/liblaunch.dylib
    0x7fff78d16000 -     0x7fff78d1affb  libmacho.dylib (900.0.1) <756F2553-07B6-3B42-ACEA-2F0F1A5E8D0F> /usr/lib/system/libmacho.dylib
    0x7fff78d1b000 -     0x7fff78d1dff3  libquarantine.dylib (86) <6AC8773F-3817-3D82-99C2-01BABB9C3CBB> /usr/lib/system/libquarantine.dylib
    0x7fff78d1e000 -     0x7fff78d1fff3  libremovefile.dylib (45) <912FA211-DD8C-3C92-8424-21B89F8B10FD> /usr/lib/system/libremovefile.dylib
    0x7fff78d20000 -     0x7fff78d37fff  libsystem_asl.dylib (356.1.1) <94972913-9DF0-3C78-847C-43E58919E3DA> /usr/lib/system/libsystem_asl.dylib
    0x7fff78d38000 -     0x7fff78d38fff  libsystem_blocks.dylib (67) <F2493BB5-B1C6-3C4D-9F1F-1B402E0F1DB7> /usr/lib/system/libsystem_blocks.dylib
    0x7fff78d39000 -     0x7fff78dc2fff  libsystem_c.dylib (1244.1.7) <2D4C21C0-9938-3552-8DC1-2C1CFAA10D38> /usr/lib/system/libsystem_c.dylib
    0x7fff78dc3000 -     0x7fff78dc6ffb  libsystem_configuration.dylib (963) <D7EFEAE6-22A0-348E-BBBE-44FFD41934FA> /usr/lib/system/libsystem_configuration.dylib
    0x7fff78dc7000 -     0x7fff78dcaffb  libsystem_coreservices.dylib (51) <21A488D0-2D07-344E-8631-CC8B2A246F35> /usr/lib/system/libsystem_coreservices.dylib
    0x7fff78dcb000 -     0x7fff78dccfff  libsystem_darwin.dylib (1244.1.7) <552C05CA-D151-3F93-B570-413340BDA9C7> /usr/lib/system/libsystem_darwin.dylib
    0x7fff78dcd000 -     0x7fff78dd3ff7  libsystem_dnssd.dylib (878.1.1) <6E28973E-A258-36F6-ACFB-259ED5885C7A> /usr/lib/system/libsystem_dnssd.dylib
    0x7fff78dd4000 -     0x7fff78e1dff7  libsystem_info.dylib (517) <483BE95B-62EB-3663-ACB3-9915A40C70F5> /usr/lib/system/libsystem_info.dylib
    0x7fff78e1e000 -     0x7fff78e43ff7  libsystem_kernel.dylib (4570.1.46) <71BA15CB-3056-3CBD-A5F5-EE61566EEA0C> /usr/lib/system/libsystem_kernel.dylib
    0x7fff78e44000 -     0x7fff78e8ffcb  libsystem_m.dylib (3146) <ABB1B85F-9FFE-31B8-AD4F-E39A30794A93> /usr/lib/system/libsystem_m.dylib
    0x7fff78e90000 -     0x7fff78eaffff  libsystem_malloc.dylib (140.1.1) <9F0745FF-B92F-330D-8812-BB74001D1D33> /usr/lib/system/libsystem_malloc.dylib
    0x7fff78eb0000 -     0x7fff78f53ff3  libsystem_network.dylib (1229.1.5) <A51EB0A4-9AA5-3F84-8E25-DD8EABE0F97E> /usr/lib/system/libsystem_network.dylib
    0x7fff78f54000 -     0x7fff78f5effb  libsystem_networkextension.dylib (767.1.4) <E842569D-00BA-3DE7-AD5A-9EF33422CF3E> /usr/lib/system/libsystem_networkextension.dylib
    0x7fff78f5f000 -     0x7fff78f68ff3  libsystem_notify.dylib (172) <98EA3D62-7C86-30DE-8261-D020D2F1EFF3> /usr/lib/system/libsystem_notify.dylib
    0x7fff78f69000 -     0x7fff78f70ff7  libsystem_platform.dylib (161) <9F5A67F7-BC65-300F-BD74-07E7732D6372> /usr/lib/system/libsystem_platform.dylib
    0x7fff78f71000 -     0x7fff78f7cff7  libsystem_pthread.dylib (301.1.6) <6D0B0110-2B44-3D3C-B672-BD08FE46378A> /usr/lib/system/libsystem_pthread.dylib
    0x7fff78f7d000 -     0x7fff78f80ffb  libsystem_sandbox.dylib (765.1.8) <EC909728-0365-3710-B00B-0BCFCE03DC71> /usr/lib/system/libsystem_sandbox.dylib
    0x7fff78f81000 -     0x7fff78f82ff3  libsystem_secinit.dylib (30) <F06ADB8F-9E94-34A7-B3C9-2C22FDD14BAD> /usr/lib/system/libsystem_secinit.dylib
    0x7fff78f83000 -     0x7fff78f8aff7  libsystem_symptoms.dylib (820.1.4) <BF0566B2-9475-3B5C-8641-7910F313BA3C> /usr/lib/system/libsystem_symptoms.dylib
    0x7fff78f8b000 -     0x7fff78f9eff7  libsystem_trace.dylib (829.1.2) <10955EBB-1AC8-3085-9A2D-F3088CA2DF71> /usr/lib/system/libsystem_trace.dylib
    0x7fff78fa0000 -     0x7fff78fa5ff7  libunwind.dylib (35.3) <6D4FCD49-D2A9-3233-95C7-A7635CE265F2> /usr/lib/system/libunwind.dylib
    0x7fff78fa6000 -     0x7fff78fd1ff7  libxpc.dylib (1205.1.10) <E7C5DB12-6D0E-3D1E-A743-F750DF112F5F> /usr/lib/system/libxpc.dylib

External Modification Summary:
  Calls made by other processes targeting this process:
    task_for_pid: 0
    thread_create: 0
    thread_set_state: 0
  Calls made by this process:
    task_for_pid: 0
    thread_create: 0
    thread_set_state: 0
  Calls made by all processes on this machine:
    task_for_pid: 697418
    thread_create: 0
    thread_set_state: 0

VM Region Summary:
ReadOnly portion of Libraries: Total=257.8M resident=0K(0%) swapped_out_or_unallocated=257.8M(100%)
Writable regions: Total=115.4M written=0K(0%) resident=0K(0%) swapped_out=0K(0%) unallocated=115.4M(100%)
 
                                VIRTUAL   REGION 
REGION TYPE                        SIZE    COUNT (non-coalesced) 
===========                     =======  ======= 
Kernel Alloc Once                    8K        2 
MALLOC                            63.2M       11 
MALLOC guard page                   16K        5 
Memory Tag 255                     1.0G       76 
Memory Tag 255 (reserved)           24K        7         reserved VM address space (unallocated)
STACK GUARD                       56.0M        8 
Stack                             40.6M        8 
__DATA                            4564K       52 
__LINKEDIT                       200.3M        8 
__TEXT                            57.5M       50 
__UNICODE                          556K        2 
shared memory                        8K        3 
===========                     =======  ======= 
TOTAL                              1.4G      220 
TOTAL, minus reserved VM space     1.4G      220 

Model: MacBookPro13,3, BootROM MBP133.0233.B00, 4 processors, Intel Core i7, 2.7 GHz, 16 GB, SMC 2.38f7
Graphics: Intel HD Graphics 530, Intel HD Graphics 530, Built-In
Graphics: Radeon Pro 460, AMD Radeon Pro 460, PCIe, 4 GB
Memory Module: BANK 0/DIMM0, 8 GB, LPDDR3, 2133 MHz, 0x802C, 0x4D5435324C31473332443450472D30393320
Memory Module: BANK 1/DIMM0, 8 GB, LPDDR3, 2133 MHz, 0x802C, 0x4D5435324C31473332443450472D30393320
AirPort: spairport_wireless_card_type_airport_extreme (0x14E4, 0x15A), Broadcom BCM43xx 1.0 (7.77.37.0.1a1)
Bluetooth: Version 6.0.0f7, 3 services, 27 devices, 1 incoming serial ports
Network Service: Wi-Fi, AirPort, en0
USB Device: USB 3.0 Bus
USB Device: iBridge
Thunderbolt Bus: MacBook Pro, Apple Inc., 22.2
Thunderbolt Bus: MacBook Pro, Apple Inc., 22.2

Build requirements for 1.1.0+?

I started getting build errors for our docker container, based on the official node.js image

Builds were fine for 1.0.2, but not 1.1.0.

Oddly, travis can build this fine, but not within our docker image. It's based off of node:8, which is based off of debian jessie: https://github.com/nodejs/docker-node/blob/4c7763dc2cb067becf12ea4bd55e88b881ccba2b/8/Dockerfile

Logs:

make: Entering directory '/usr/src/app/node_modules/isolated-vm/build'
  CXX(target) Release/obj.target/isolated_vm/src/isolate/allocator.o
In file included from ../src/isolate/allocator.cc:2:0:
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::data' [-Wmissing-field-initializers]
   v8::StartupData startup_data {};
                                 ^
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::raw_size' [-Wmissing-field-initializers]
  CXX(target) Release/obj.target/isolated_vm/src/isolate/class_handle.o
In file included from ../src/isolate/remote_handle.h:3:0,
                 from ../src/isolate/util.h:6,
                 from ../src/isolate/convert_param.h:3,
                 from ../src/isolate/class_handle.h:4,
                 from ../src/isolate/class_handle.cc:1:
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::data' [-Wmissing-field-initializers]
   v8::StartupData startup_data {};
                                 ^
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::raw_size' [-Wmissing-field-initializers]
  CXX(target) Release/obj.target/isolated_vm/src/isolate/environment.o
In file included from ../src/isolate/environment.cc:1:0:
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::data' [-Wmissing-field-initializers]
   v8::StartupData startup_data {};
                                 ^
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::raw_size' [-Wmissing-field-initializers]
  CXX(target) Release/obj.target/isolated_vm/src/isolate/holder.o
In file included from ../src/isolate/holder.cc:2:0:
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::data' [-Wmissing-field-initializers]
   v8::StartupData startup_data {};
                                 ^
../src/isolate/environment.h:205:33: warning: missing initializer for member 'v8::StartupData::raw_size' [-Wmissing-field-initializers]
../src/isolate/holder.cc: In member function 'void ivm::IsolateHolder::Dispose()':
../src/isolate/holder.cc:14:102: error: no matching function for call to 'atomic_exchange(std::shared_ptr<ivm::IsolateEnvironment>*, std::shared_ptr<ivm::IsolateEnvironment>)'
  shared_ptr<IsolateEnvironment> tmp = std::atomic_exchange(&isolate, shared_ptr<IsolateEnvironment>());
                                                                                                      ^
../src/isolate/holder.cc:14:102: note: candidates are:
In file included from ../src/isolate/environment.h:8:0,
                 from ../src/isolate/holder.cc:2:
/usr/include/c++/4.9/atomic:914:5: note: template<class _ITp> _ITp std::atomic_exchange(std::atomic<_ITp>*, _ITp)
     atomic_exchange(atomic<_ITp>* __a, _ITp __i) noexcept
     ^
/usr/include/c++/4.9/atomic:914:5: note:   template argument deduction/substitution failed:
../src/isolate/holder.cc:14:102: note:   'std::shared_ptr<ivm::IsolateEnvironment>' is not derived from 'std::atomic<_ITp>'
  shared_ptr<IsolateEnvironment> tmp = std::atomic_exchange(&isolate, shared_ptr<IsolateEnvironment>());
                                                                                                      ^
In file included from ../src/isolate/environment.h:8:0,
                 from ../src/isolate/holder.cc:2:
/usr/include/c++/4.9/atomic:919:5: note: template<class _ITp> _ITp std::atomic_exchange(volatile std::atomic<_ITp>*, _ITp)
     atomic_exchange(volatile atomic<_ITp>* __a, _ITp __i) noexcept
     ^
/usr/include/c++/4.9/atomic:919:5: note:   template argument deduction/substitution failed:
../src/isolate/holder.cc:14:102: note:   'std::shared_ptr<ivm::IsolateEnvironment>' is not derived from 'volatile std::atomic<_ITp>'
  shared_ptr<IsolateEnvironment> tmp = std::atomic_exchange(&isolate, shared_ptr<IsolateEnvironment>());
                                                                                                      ^
../src/isolate/holder.cc: In member function 'std::shared_ptr<ivm::IsolateEnvironment> ivm::IsolateHolder::GetIsolate()':
../src/isolate/holder.cc:24:34: error: no matching function for call to 'atomic_load(std::shared_ptr<ivm::IsolateEnvironment>*)'
  return std::atomic_load(&isolate);
                                  ^
../src/isolate/holder.cc:24:34: note: candidates are:
In file included from ../src/isolate/environment.h:8:0,
                 from ../src/isolate/holder.cc:2:
/usr/include/c++/4.9/atomic:904:5: note: template<class _ITp> _ITp std::atomic_load(const std::atomic<_ITp>*)
     atomic_load(const atomic<_ITp>* __a) noexcept
     ^
/usr/include/c++/4.9/atomic:904:5: note:   template argument deduction/substitution failed:
../src/isolate/holder.cc:24:34: note:   'std::shared_ptr<ivm::IsolateEnvironment>' is not derived from 'const std::atomic<_ITp>'
  return std::atomic_load(&isolate);
                                  ^
In file included from ../src/isolate/environment.h:8:0,
                 from ../src/isolate/holder.cc:2:
/usr/include/c++/4.9/atomic:909:5: note: template<class _ITp> _ITp std::atomic_load(const volatile std::atomic<_ITp>*)
     atomic_load(const volatile atomic<_ITp>* __a) noexcept
     ^
/usr/include/c++/4.9/atomic:909:5: note:   template argument deduction/substitution failed:
../src/isolate/holder.cc:24:34: note:   'std::shared_ptr<ivm::IsolateEnvironment>' is not derived from 'const volatile std::atomic<_ITp>'
  return std::atomic_load(&isolate);
                                  ^
../src/isolate/holder.cc: In member function 'void ivm::IsolateHolder::ScheduleTask(std::unique_ptr<ivm::Runnable>, bool, bool)':
../src/isolate/holder.cc:28:64: error: no matching function for call to 'atomic_load(std::shared_ptr<ivm::IsolateEnvironment>*)'
  shared_ptr<IsolateEnvironment> ref = std::atomic_load(&isolate);
                                                                ^
../src/isolate/holder.cc:28:64: note: candidates are:
In file included from ../src/isolate/environment.h:8:0,
                 from ../src/isolate/holder.cc:2:
/usr/include/c++/4.9/atomic:904:5: note: template<class _ITp> _ITp std::atomic_load(const std::atomic<_ITp>*)
     atomic_load(const atomic<_ITp>* __a) noexcept
     ^
/usr/include/c++/4.9/atomic:904:5: note:   template argument deduction/substitution failed:
../src/isolate/holder.cc:28:64: note:   'std::shared_ptr<ivm::IsolateEnvironment>' is not derived from 'const std::atomic<_ITp>'
  shared_ptr<IsolateEnvironment> ref = std::atomic_load(&isolate);
                                                                ^
In file included from ../src/isolate/environment.h:8:0,
                 from ../src/isolate/holder.cc:2:
/usr/include/c++/4.9/atomic:909:5: note: template<class _ITp> _ITp std::atomic_load(const volatile std::atomic<_ITp>*)
     atomic_load(const volatile atomic<_ITp>* __a) noexcept
     ^
/usr/include/c++/4.9/atomic:909:5: note:   template argument deduction/substitution failed:
../src/isolate/holder.cc:28:64: note:   'std::shared_ptr<ivm::IsolateEnvironment>' is not derived from 'const volatile std::atomic<_ITp>'
  shared_ptr<IsolateEnvironment> ref = std::atomic_load(&isolate);
                                                                ^
../src/isolate/holder.cc: In member function 'std::shared_ptr<ivm::IsolateEnvironment> ivm::IsolateHolder::GetIsolate()':
../src/isolate/holder.cc:25:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
isolated_vm.target.mk:115: recipe for target 'Release/obj.target/isolated_vm/src/isolate/holder.o' failed
make: Leaving directory '/usr/src/app/node_modules/isolated-vm/build'
make: *** [Release/obj.target/isolated_vm/src/isolate/holder.o] Error 1
gyp ERR! build error 
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:258:23)
gyp ERR! stack     at emitTwo (events.js:126:13)
gyp ERR! stack     at ChildProcess.emit (events.js:214:7)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12)
gyp ERR! System Linux 4.4.0-101-generic
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--release"
gyp ERR! cwd /usr/src/app/node_modules/isolated-vm
gyp ERR! node -v v8.9.4
gyp ERR! node-gyp -v v3.6.2
gyp ERR! not ok 

index.js references debug build

The index.js file references the debug build:

module.exports = require('./build/Debug/isolated_vm').ivm;

When installed thru npm, the build is stored in the Release directory and thus does not work.

Segfault during "isolate environment executor unlock"

Just ran into this segfault (again, under some load, but limiting context creation to 1 at a time.)

PID 96350 received SIGSEGV for address: 0x20
0   segfault-handler.node               0x0000000105c44168 _ZL16segfault_handleriP9__siginfoPv + 280
1   libsystem_platform.dylib            0x00007fffe68deb3a _sigtramp + 26
2   ???                                 0x00007000048bd888 0x0 + 123145378584712
3   isolated_vm.node                    0x0000000104b84b60 _ZN3ivm18IsolateEnvironment8Executor6UnlockD1Ev + 32
4   isolated_vm.node                    0x0000000104b93864 _ZN3ivm14ThreePhaseTask7RunSyncERNS_13IsolateHolderEb + 436
5   isolated_vm.node                    0x0000000104bc494f _ZN3ivm14ThreePhaseTask3RunILi4ENS_11ApplyRunnerEJRNS_15ReferenceHandleERN2v810MaybeLocalINS5_5ValueEEERNS6_INS5_5ArrayEEERNS6_INS5_6ObjectEEERNSt3__110shared_ptrINS_12RemoteHandleINS5_7ContextEEEEERNSH_INSI_IS7_EEEEEEENS5_5LocalIS7_EERNS_13IsolateHolderEDpOT1_ + 175
6   isolated_vm.node                    0x0000000104bc4a47 _ZN3ivm14FunctorRunners11RunCallbackIZNS_11ClassHandle17ParameterizeEntryILin1EPFN2v85LocalINS4_5ValueEEEPNS_15ReferenceHandleENS4_10MaybeLocalIS6_EENSA_INS4_5ArrayEEENSA_INS4_6ObjectEEEEXadL_ZNS2_10MethodCastIMS8_FS7_SB_SD_SF_EE6InvokeIXadL_ZNS8_5ApplyILi4EEES7_SB_SD_SF_EEEES7_S9_SB_SD_SF_EEEEvRKNS4_20FunctionCallbackInfoIS6_EEEUlvE_SQ_EEvRT0_T_ + 135
7   node                                0x00000001001f9260 _ZN2v88internal25FunctionCallbackArguments4CallEPFvRKNS_20FunctionCallbackInfoINS_5ValueEEEE + 416
8   node                                0x0000000100261580 _ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE + 800
9   node                                0x0000000100260be3 _ZN2v88internalL26Builtin_Impl_HandleApiCallENS0_16BuiltinArgumentsEPNS0_7IsolateE + 259
10  ???                                 0x00003a1fc788463d 0x0 + 63908165994045
11  ???                                 0x00003a1fc79742ea 0x0 + 63908166976234
libc++abi.dylib: terminating

I tried with the latest master (at the time of this writing: 242e864 too. I'm using node 8.9.4 from your tailcall-backport branch.

Segfault regarding TypedArrays

I got this while playing with probably-too-large ArrayBuffer allocations:

PID 23911 received SIGSEGV for address: 0x0
0   segfault-handler.node               0x00000001038f3168 _ZL16segfault_handleriP9__siginfoPv + 280
1   libsystem_platform.dylib            0x00007fffe68deb3a _sigtramp + 26
2   ???                                 0x0000000105950600 0x0 + 4388619776
3   node                                0x0000000100559129 _ZN2v88internal12JSTypedArray22MaterializeArrayBufferENS0_6HandleIS1_EE + 601
4   node                                0x000000010068a562 _ZN2v88internal27Runtime_TypedArrayGetBufferEiPPNS0_6ObjectEPNS0_7IsolateE + 70
5   ???                                 0x0000141d30846eb8 0x0 + 22115600592568

I couldn't find the exact cause of this, I worked around the issue another way. In the past you've been able to fix segfaults from these traces without much context, so maybe you can do the same this time :)

Segfault in SessionHandle and another unknown symbol

I think I reported this in a comment, but I figured I'd make it official with an issue of its own:

This seems to happen when disposing of an isolate (sometimes) when putting way too much load on it.

PID 18013 received SIGSEGV for address: 0x118
0   segfault-handler.node               0x0000000103fed168 _ZL16segfault_handleriP9__siginfoPv + 280
1   libsystem_platform.dylib            0x00007fffc3515b3a _sigtramp + 26
2   isolated_vm.node                    0x0000000106b2ca48 _ZGVZN3ivm13SessionHandle16TemplateSpecificEvE4tmpl + 42544
3   node                                0x000000010046e302 _ZN2v88internal4Heap19ExternalStringTable8TearDownEv + 260
4   node                                0x000000010046e05d _ZN2v88internal4Heap8TearDownEv + 543
5   node                                0x00000001004f0979 _ZN2v88internal7Isolate6DeinitEv + 557
6   node                                0x00000001004f0697 _ZN2v88internal7Isolate8TearDownEv + 77
7   isolated_vm.node                    0x0000000106ace8fe _ZN3ivm18IsolateEnvironmentD2Ev + 958
8   libc++.1.dylib                      0x00007fffc1ed1dae _ZNSt3__119__shared_weak_count16__release_sharedEv + 44
9   isolated_vm.node                    0x0000000106acbc12 _ZN3ivm18IsolateEnvironment9Scheduler17AsyncCallbackPoolEbPv + 82
10  isolated_vm.node                    0x0000000106ad0608 _ZZN13thread_pool_t10new_threadEvENKUlvE_clEv + 136
11  isolated_vm.node                    0x0000000106ad050d _ZNSt3__114__thread_proxyINS_5tupleIJNS_10unique_ptrINS_15__thread_structENS_14default_deleteIS3_EEEEZN13thread_pool_t10new_threadEvEUlvE_EEEEEPvSA_ + 45
12  libsystem_pthread.dylib             0x00007fffc351f93b _pthread_body + 180
13  libsystem_pthread.dylib             0x00007fffc351f887 _pthread_body + 0
14  libsystem_pthread.dylib             0x00007fffc351f08d thread_start + 13

I've also gotten this one, also related to disposition.

PID 18114 received SIGSEGV for address: 0x118
0   segfault-handler.node               0x00000001038f5168 _ZL16segfault_handleriP9__siginfoPv + 280
1   libsystem_platform.dylib            0x00007fffc3515b3a _sigtramp + 26
2   ???                                 0x0000000000000000 0x0 + 0
3   node                                0x000000010046e26e _ZN2v88internal4Heap19ExternalStringTable8TearDownEv + 112
4   node                                0x000000010046e05d _ZN2v88internal4Heap8TearDownEv + 543
5   node                                0x00000001004f0979 _ZN2v88internal7Isolate6DeinitEv + 557
6   node                                0x00000001004f0697 _ZN2v88internal7Isolate8TearDownEv + 77
7   isolated_vm.node                    0x00000001062048fe _ZN3ivm18IsolateEnvironmentD2Ev + 958
8   libc++.1.dylib                      0x00007fffc1ed1dae _ZNSt3__119__shared_weak_count16__release_sharedEv + 44
9   isolated_vm.node                    0x0000000106201c12 _ZN3ivm18IsolateEnvironment9Scheduler17AsyncCallbackPoolEbPv + 82
10  isolated_vm.node                    0x0000000106206608 _ZZN13thread_pool_t10new_threadEvENKUlvE_clEv + 136
11  isolated_vm.node                    0x000000010620650d _ZNSt3__114__thread_proxyINS_5tupleIJNS_10unique_ptrINS_15__thread_structENS_14default_deleteIS3_EEEEZN13thread_pool_t10new_threadEvEUlvE_EEEEEPvSA_ + 45
12  libsystem_pthread.dylib             0x00007fffc351f93b _pthread_body + 180
13  libsystem_pthread.dylib             0x00007fffc351f887 _pthread_body + 0
14  libsystem_pthread.dylib             0x00007fffc351f08d thread_start + 13
libc++abi.dylib: terminating

I do my best checking is isolate is disposed at every step of the way, but sometimes the isolate will be disposed between my check and global.set or any other function interacting with the isolate.

Timeout does not trigger if something is registered on the event loop

'use strict';
let ivm = require('isolated-vm');

setTimeout(() => {
    console.log('timeout');
}, 1000);

(async function() {
	let isolate = new ivm.Isolate;
	let context = isolate.createContextSync();
	let global = context.globalReference();
	global.setSync('global', global.derefInto());
	isolate.compileScriptSync('global.run = () => { for(;;); }').runSync(context);
	let run = global.getSync('run');
	try {
		run.applySync(undefined, [], { timeout: 20 });
		console.log('what?');
	} catch (err) {}

	try {
		await run.apply(undefined, [], { timeout: 20 });
		console.log('what?');
	} catch (err) {
		console.log('pass');
	}
})().catch(console.error);

Output:

<1 sec>
timeout
pass

Segfault in Isolate Environment AsyncCallbackPool

I don't have the trace unfortunately. The segfault-handler package doesn't always catch it.

Here's the log line just before the process crashed (exit code 134):

node: ../src/isolate/environment.cc:171: static void ivm::IsolateEnvironment::Scheduler::AsyncCallbackPool(bool, void*): Assertion `root_async.data == nullptr' failed.

This is the only line I could find, this is in production. Our process promptly restarted afterwards.

Mutex lock failed: Invalid argument

I'm getting a misleading error with this basic setup:

const ivm = require('isolated-vm');
const isolate = new ivm.Isolate();
const context = isolate.createContextSync();
const script = isolate.compileScriptSync('');
script.runSync(context);

Error:

libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument

Snapshot Isolate

I would like to create snapshots of already created isolates.
This can be useful if the isolate contains a state that can be modified with the exposed functions and which should be reused at repeated execution.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.