Skip to content

Add bounds check to source map VLQ decoder shift#8331

Merged
kripken merged 2 commits intoWebAssembly:mainfrom
sumleo:fix-vlq-shift-overflow
Feb 19, 2026
Merged

Add bounds check to source map VLQ decoder shift#8331
kripken merged 2 commits intoWebAssembly:mainfrom
sumleo:fix-vlq-shift-overflow

Conversation

@sumleo
Copy link
Contributor

@sumleo sumleo commented Feb 16, 2026

Summary

  • readBase64VLQ() increments shift by 5 for each continuation digit with no upper bound.
  • After 7 continuation digits, shift reaches 35 and digit << shift on a uint32_t is undefined behavior (shifting by >= type width).
  • Added a bounds check after incrementing shift, throwing MapParseException for malformed VLQ values with too many continuation digits.

Test plan

  • All existing unit tests pass (309/309), including the source map test suite.
  • This is a defensive check for malformed input that would previously trigger UB.

readBase64VLQ() increments the shift value by 5 for each continuation
digit with no upper bound. After 7 continuation digits, shift reaches 35
and 'digit << shift' on a uint32_t is undefined behavior (shifting by
an amount >= the type width). Add a bounds check after incrementing
shift, throwing a MapParseException for malformed VLQ values.
@kripken
Copy link
Member

kripken commented Feb 17, 2026

This looks valid, but we only have limited testing of source maps in this repo. My worry is if this invalid pattern is common in the wild, and it was silently ignored, while this PR would make things break.

A good way to check would be to run the emscripten test suite with binaryen built with this PR, at least all tests with "debug", "source", or "dwarf" in the name.

Another option might be to generate a source map that errors on this PR, then see what happens without this PR - if it was erroring anyhow, I would be less worried.

Add a test case in BadSourceMaps verifying that a VLQ with 7 continuation
digits is rejected with "VLQ value too large". This exercises the bounds
check that prevents undefined behavior from shifting a uint32_t by >= 32.
@sumleo
Copy link
Contributor Author

sumleo commented Feb 19, 2026

Good point. I've added a test case (in BadSourceMaps) and thought through the impact:

Why this shouldn't break valid source maps:

Base64 VLQ uses 5 data bits per character. A 7-continuation-digit VLQ encodes values >= 2^30 (~1 billion). Source map VLQ values represent line/column offsets and source/name indices — these are small numbers in practice (lines rarely exceed millions, columns rarely exceed thousands). Legitimate source maps should never need more than 5-6 characters per VLQ value.

What happens today without the fix:

On x86, digit << 35 wraps to digit << 3 (shift mod 32), silently producing a wrong accumulated value. This corrupted value then propagates as a wrong file/line/column/symbol index. So the current behavior for such a malformed VLQ is already "broken" — it just fails silently instead of explicitly. The change here makes the failure mode go from silent corruption to an explicit exception.

Re: running emscripten tests:

I don't currently have an emscripten setup to test against. If you'd prefer, I could change this to a warning + truncation instead of throwing, or you could run it through CI to see if anything breaks. But given the math, I believe any source map that would hit this was already producing wrong debug info silently.

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I think this is good. That test + some emcc tests I ran now suggest to me that this is safe.

@kripken kripken merged commit 74a471c into WebAssembly:main Feb 19, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments