nodejs / nodejs/node

SourceMap.findEntry() returns a stale name for a final mapping segment that has no name index

Open
#63,795 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

source maps stale
Dominant language
JavaScript
Stars
122k
Forks
37.3k
Avg merge
4d 2h
Merged PRs (30d)
283

Description

Version

v24.8.0 (present on main; introduced in v15.4.0 / v14.17.1)

Platform
all
Subsystem

module / source maps

What steps will reproduce the bug?

The simplest way to recreate the issue is:

const { SourceMap } = require('node:module');
const sm = new SourceMap({
  version: 3, sources: ['a.js'], names: ['foo'], mappings: 'AAAAA,CAAC',
});
console.log(sm.findEntry(0, 1).name); // prints 'foo'
How often does it reproduce? Is there a required condition?

Fails on every run, deterministically.

Required condition: the source map's mappings string must end with a location-only segment (4 fields, no name index), and some earlier segment must have set a name index (i.e. names is non-empty and was referenced before theend). When both hold, findEntry() on that final segment returns the last-seen name instead of undefined

Both conditions are the norm for real minified bundles, so in practice it triggers on essentially every production source map. The minimal repro above (mappings: 'AAAAA,CAAC') is the smallest case that satisfies it.

With --enable-source-maps, the bottom global frame of a stack trace inherits the last-named function instead of being anonymous. Empirically: absent in v14.17.0, appears in v14.17.1 (and v15.4.0), introduced by #36042.

What is the expected behavior? Why is that the expected behavior?

The second segment (CAAC) has only 4 fields and no name index, so findEntry(0, 1).name should be undefined.

What do you see instead?

It returns 'foo', the name from the previous segment.

Additional information

In practice this leads to stack traces being subtly wrong: under --enable-source-maps, the bottom global/anonymous frame is labeled with the last-named function instead of being anonymous. The wrong name is a real identifier from the program, so it looks correct and misleads during debugging rather than being noticed as a defect.

The bug is in SourceMap's #parseMap, it reads the optional 5th (name) VLQ guarded only by !isSeparator(stringCharIterator.peek()). At the end of the mappings string peek() returns '', which is not a separator, so it decodes a phantom VLQ (0), keeps the previous nameIndex, and assigns names[nameIndex] to a segment with no name.

I've implemented a one line fix and I'll open a PR and link it here as soon as it's ready.

PR Link: https://github.com/nodejs/node/pull/63801

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the JavaScript snippet in the report, then inspect SourceMap's #parseMap handling of the optional name field. Done means a final location-only mapping returns an undefined name rather than reusing the previous name, with regression coverage for the minimal mapping example.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.