Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

process: fix two overflow cases in SourceMap VLQ decoding #31490

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/internal/source_map/source_map.js
Expand Up @@ -303,8 +303,19 @@ function decodeVLQ(stringCharIterator) {

// Fix the sign.
const negative = result & 1;
result >>= 1;
return negative ? -result : result;
// Use unsigned right shift, so that the 32nd bit is properly shifted to the
cjihrig marked this conversation as resolved.
Show resolved Hide resolved
// 31st, and the 32nd becomes unset.
result >>>= 1;
if (!negative) {
return result;
}

// We need to OR here to ensure the 32nd bit (the sign bit in an Int32) is
// always set for negative numbers. If `result` were 1, (meaning `negate` is
// true and all other bits were zeros), `result` would now be 0. But -0
// doesn't flip the 32nd bit as intended. All other numbers will successfully
// set the 32nd bit without issue, so doing this is a noop for them.
return -result | (1 << 31);
}

/**
Expand Down
42 changes: 42 additions & 0 deletions test/parallel/test-source-map-api.js
Expand Up @@ -82,3 +82,45 @@ const { readFileSync } = require('fs');
assert.strictEqual(payload.sources[0], sourceMap.payload.sources[0]);
assert.notStrictEqual(payload.sources, sourceMap.payload.sources);
}

// Test various known decodings to ensure decodeVLQ works correctly.
{
function makeMinimalMap(column) {
return {
sources: ['test.js'],
// Mapping from the 0th line, 0th column of the output file to the 0th
// source file, 0th line, ${column}th column.
mappings: `AAA${column}`,
};
}
const knownDecodings = {
'A': 0,
'B': -2147483648,
'C': 1,
'D': -1,
'E': 2,
'F': -2,

// 2^31 - 1, maximum values
'+/////D': 2147483647,
'8/////D': 2147483646,
'6/////D': 2147483645,
'4/////D': 2147483644,
'2/////D': 2147483643,
'0/////D': 2147483642,

// -2^31 + 1, minimum values
'//////D': -2147483647,
'9/////D': -2147483646,
'7/////D': -2147483645,
'5/////D': -2147483644,
'3/////D': -2147483643,
'1/////D': -2147483642,
};

for (const column in knownDecodings) {
const sourceMap = new SourceMap(makeMinimalMap(column));
const { originalColumn } = sourceMap.findEntry(0, 0);
assert.strictEqual(originalColumn, knownDecodings[column]);
}
}