Skip to content

vm: vm.compileFunction does not support negative lineOffset and columnOffset #49848

Closed
@wmtdru8xip

Description

Version:

v20.6.1; older versions are also affected

Subsystem

vm

Current issue:

The vm.compileFunction method in Node.js currently doesn't support negative lineOffset and columnOffset values.

  1. An error is thrown when the value is negative, as demonstrated below:
let { compileFunction } = require('node:vm');

try {
	compileFunction('', [], { lineOffset: -1 });
} catch (exception) {
	console.log(exception.stack);
}
/*
RangeError [ERR_OUT_OF_RANGE]: The value of 'options.lineOffset' is out of range. It must be >= 0 && <= 4294967295. Received -1
    at internalCompileFunction (node:internal/vm:50:3)
    at Module.compileFunction (node:vm:302:10)
    at ...
*/
  1. An overflow occurs when the value is passed to the V8 engine if it is larger than (2^31)-1, causing it to become -2147483637 with the presence of overflow protection measures:
compileFunction('console.trace()', [], {
	lineOffset: 3456789012,
})();
/*
Trace
    at <anonymous>:-2147483648:9
    at ...
*/

Additional information:

The vm.compileFunction method in Node.js currently doesn't support negative lineOffset and columnOffset values. This is inconsistent with other methods such as vm.runInContext, vm.runInNewContext, and new vm.Script(), which do support negative values for these parameters.

The underlying V8 engine also supports negative values for these parameters as indicated in the ScriptOrigin class:

class V8_EXPORT ScriptOrigin {
 public:
  V8_INLINE ScriptOrigin(Isolate* isolate, Local<Value> resource_name,
                         int resource_line_offset = 0,
                         int resource_column_offset = 0,
                         bool resource_is_shared_cross_origin = false,
                         int script_id = -1,

A common use case for the lineOffset option in vm.compileFunction is to shift lines in error tracebacks to support adding wrappers. Negative values enable the correct line number to be displayed in stack traces when wrapper code is added, while positive values would require part of the code to be removed to get a correct line number, which is not applicable in most scenarios. Hence, negative options are a primary use case that should be supported. An example use case is shown below:

function compileAsyncFunction (code, params, options) {
	if (!params) {
		params = [];
	}
	if (!options) {
		if (!params.join) {
			options = params;
			params = [];
		} else {
			options = {};
		}
	}
	options = {
		...options, lineOffset: (options.lineOffset ?? 0) - 1
	};
	return vm.compileFunction("(async (" + params.join(", ") + ") => {\n" + code + "\n}).apply(null, arguments)", params, options);
}

compileAsyncFunction(scriptFileContentReadFromDisk)();

Possible solution:

Upon investigation, the cause of this issue can be traced back to this line in lib/internal/vm.js:

validateUint32(lineOffset, 'options.lineOffset');

A tested and effective solution is to change the validateUint32 function to validateInt32 for the lineOffset and columnOffset parameters.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugIssues with confirmed bugs.good first issueIssues that are suitable for first-time contributors.vmIssues and PRs related to the vm subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions