vm
: vm.compileFunction
does not support negative lineOffset
and columnOffset
#49848
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.
- 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 ...
*/
- 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
:
Line 50 in 448996c
A tested and effective solution is to change the validateUint32
function to validateInt32
for the lineOffset
and columnOffset
parameters.
Activity