summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2019-11-02 18:33:38 +0100
committerAnna Henningsen <anna@addaleax.net>2019-11-05 20:55:54 +0100
commitf17e414dc4b5d80dd5b5c7ee7107659ec5ebeb1a (patch)
tree18e68a23f781aa3c018c5fa315885423fe4fcaa7 /lib
parenta4123a86ef5e951d9f193d3ec331759b1b4fdee1 (diff)
downloadandroid-node-v8-f17e414dc4b5d80dd5b5c7ee7107659ec5ebeb1a.tar.gz
android-node-v8-f17e414dc4b5d80dd5b5c7ee7107659ec5ebeb1a.tar.bz2
android-node-v8-f17e414dc4b5d80dd5b5c7ee7107659ec5ebeb1a.zip
process: make source map getter resistant against prototype tampering
Since this code runs during process and Worker shutdown, it should not call user-provided code and thereby e.g. provide a way to break out of `worker.terminate()`. PR-URL: https://github.com/nodejs/node/pull/30228 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Ben Coe <bencoe@gmail.com> Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/source_map/source_map_cache.js63
1 files changed, 51 insertions, 12 deletions
diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js
index 0e77203e9d..340615eb6c 100644
--- a/lib/internal/source_map/source_map_cache.js
+++ b/lib/internal/source_map/source_map_cache.js
@@ -1,5 +1,28 @@
'use strict';
+const {
+ JSON,
+ Object: {
+ create: ObjectCreate,
+ keys: ObjectKeys,
+ getOwnPropertyDescriptor: ObjectGetOwnPropertyDescriptor,
+ },
+ ObjectPrototype: {
+ hasOwnProperty: ObjectHasOwnProperty
+ },
+ MapPrototype: {
+ entries: MapEntries
+ }, uncurryThis
+} = primordials;
+
+const MapIteratorNext = uncurryThis(MapEntries(new Map()).next);
+const WeakMapGet = uncurryThis(WeakMap.prototype.get);
+
+function ObjectGetValueSafe(obj, key) {
+ const desc = ObjectGetOwnPropertyDescriptor(obj, key);
+ return ObjectHasOwnProperty(desc, 'value') ? desc.value : undefined;
+}
+
// See https://sourcemaps.info/spec.html for SourceMap V3 specification.
const { Buffer } = require('buffer');
const debug = require('internal/util/debuglog').debuglog('source_map');
@@ -9,7 +32,6 @@ const { getOptionValue } = require('internal/options');
const {
normalizeReferrerURL,
} = require('internal/modules/cjs/helpers');
-const { JSON, Object } = primordials;
// For cjs, since Module._cache is exposed to users, we use a WeakMap
// keyed on module, facilitating garbage collection.
const cjsSourceMapCache = new WeakMap();
@@ -17,6 +39,7 @@ const cjsSourceMapCache = new WeakMap();
// on filenames.
const esmSourceMapCache = new Map();
const { fileURLToPath, URL } = require('url');
+let Module;
let experimentalSourceMaps;
function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
@@ -40,6 +63,7 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
const data = dataFromUrl(basePath, match.groups.sourceMappingURL);
const url = data ? null : match.groups.sourceMappingURL;
if (cjsModuleInstance) {
+ if (!Module) Module = require('internal/modules/cjs/loader').Module;
cjsSourceMapCache.set(cjsModuleInstance, {
filename,
lineLengths: lineLengths(content),
@@ -148,17 +172,27 @@ function rekeySourceMap(cjsModuleInstance, newInstance) {
}
}
+// WARNING: The `sourceMapCacheToObject` and `appendCJSCache` run during
+// shutdown. In particular, they also run when Workers are terminated, making
+// it important that they do not call out to any user-provided code, including
+// built-in prototypes that might have been tampered with.
+
// Get serialized representation of source-map cache, this is used
// to persist a cache of source-maps to disk when NODE_V8_COVERAGE is enabled.
function sourceMapCacheToObject() {
- const obj = Object.create(null);
+ const obj = ObjectCreate(null);
- for (const [k, v] of esmSourceMapCache) {
+ const it = MapEntries(esmSourceMapCache);
+ let entry;
+ while (!(entry = MapIteratorNext(it)).done) {
+ const k = entry.value[0];
+ const v = entry.value[1];
obj[k] = v;
}
+
appendCJSCache(obj);
- if (Object.keys(obj).length === 0) {
+ if (ObjectKeys(obj).length === 0) {
return undefined;
} else {
return obj;
@@ -171,23 +205,28 @@ function sourceMapCacheToObject() {
// TODO(bcoe): this means we don't currently serialize source-maps attached
// to error instances, only module instances.
function appendCJSCache(obj) {
- const { Module } = require('internal/modules/cjs/loader');
- Object.keys(Module._cache).forEach((key) => {
- const value = cjsSourceMapCache.get(Module._cache[key]);
+ if (!Module) return;
+ const cjsModuleCache = ObjectGetValueSafe(Module, '_cache');
+ const cjsModules = ObjectKeys(cjsModuleCache);
+ for (let i = 0; i < cjsModules.length; i++) {
+ const key = cjsModules[i];
+ const module = ObjectGetValueSafe(cjsModuleCache, key);
+ const value = WeakMapGet(cjsSourceMapCache, module);
if (value) {
+ // This is okay because `obj` has a null prototype.
obj[`file://${key}`] = {
- lineLengths: value.lineLengths,
- data: value.data,
- url: value.url
+ lineLengths: ObjectGetValueSafe(value, 'lineLengths'),
+ data: ObjectGetValueSafe(value, 'data'),
+ url: ObjectGetValueSafe(value, 'url')
};
}
- });
+ }
}
// Attempt to lookup a source map, which is either attached to a file URI, or
// keyed on an error instance.
function findSourceMap(uri, error) {
- const { Module } = require('internal/modules/cjs/loader');
+ if (!Module) Module = require('internal/modules/cjs/loader').Module;
let sourceMap = cjsSourceMapCache.get(Module._cache[uri]);
if (!uri.startsWith('file://')) uri = normalizeReferrerURL(uri);
if (sourceMap === undefined) {