Extension host OOM crash: glob.ts compiles an arbitrarily long watcher pattern into a pathological regex (regExp.test blows the V8 heap)

Open
#321,548 0 comments 0 reactions 1 assignee View on GitHub

@dmitrivMS is already working on this.

Since Jun 16, 2026.

Assessment

This issue has not been assessed yet.

Description

bug file-glob

Does this issue occur when all extensions are disabled?: Yes

  • VS Code Version: VS Code 1.124.2 (commit 6928394f91b684055b873eecb8bc281365131f1c)
  • OS Version: macOS 26.5.1 (arm64)

Note: local username scrubbed to userx in all captured strings.

Does this issue occur when all extensions are disabled?: No (end-to-end), but the crash is reproducible WITH extensions disabled

The end-to-end crash is triggered by a language extension (gopls / vscode-go) supplying a bad watch pattern, so disabling all extensions removes the trigger. However, the defect itself is in VS Code core (vs/base/common/glob.ts) and is reproducible with no extensions at all by feeding the attached pattern directly to the glob API (see Steps to Reproduce) — glob.parse(pattern) + a single match(path) OOMs the host. So this is not an "extension bug" to forward to a publisher: the core defect is that glob.ts compiles an arbitrary, untrusted, multi-kilobyte string into a regex and runs it on every file event with no length or complexity guard, so a single malformed pattern from any extension can OOM-kill the entire extension host.

  • VS Code Version: 1.124.2 (6928394f91b684055b873eecb8bc281365131f1c)
  • OS Version: macOS 26.5.1, arm64
  • Insiders: the faulting code path (glob.ts parseRegExp / regExp.test) is unchanged on main, so this is expected to reproduce on the latest Insiders as well.

Summary

A file-watcher glob pattern that is not a real glob — in my case the multi-line error output of a failed go list (~6.5 KB, dozens of missing go.sum entry ... lines) — gets compiled by glob.ts into an 8,691-character regex containing 459 [/\\] path-separator character classes. On the very next file-system event, extHostFileSystemEventService.$onFileEvent runs regExp.test(path) against that regex, which exhibits catastrophic backtracking / unbounded memory growth and OOM-crashes the extension host.

Because this happens inside the extension host, it silently kills any in-flight extension work. In my case it repeatedly cancelled in-flight GitHub Copilot Chat requests (they surfaced as "Canceled"), which is how I found it — but the editor-level defect is independent of Copilot.

The root problem is defense-in-depth: glob.ts trusts the pattern string completely. A malformed pattern should fail safe (be rejected / produce a non-matching matcher), never OOM the host.

Steps to Reproduce

I have preserved the exact captured pattern and compiled regExp as a minimal repro. The shortest faithful reproduction:

import { parse } from 'vs/base/common/glob';

// `pattern` = the ~6.5 KB non-glob string captured from the watcher
// (the literal multi-line output of a failed `go list`, full of
//  "missing go.sum entry for module ... to add: go get ..." lines)
const pattern = /* contents of the attached `pattern` file */;

const match = parse(pattern);              // compiles to an ~8.6 KB regex
match('/users/userx/go/src/app/some/package/file.go');
// → catastrophic backtracking → V8 heap OOM

How it arises organically (real-world trigger):

  1. Open a Go workspace where go list ./... fails (e.g. missing go.sum entries for an imported module).
  2. gopls / vscode-go surfaces that failure string and it ends up registered as a file-watch glob pattern (a 6.5 KB multi-line string — clearly not a glob).
  3. glob.ts compiles it into the pathological regex above.
  4. Save any .go file → $onFileEventregExp.test(path) → extension host OOM.

Smoking gun (extension-host debugger)

Running Developer: Show Running Extensions with a profile attached and reproducing the crash, the debugger paused with the banner "PAUSED BEFORE OUT OF MEMORY EXCEPTION". Faulting frame and call stack (top-down):

<anonymous>                         glob.ts                            ← regExp.test(path) → OOM
n                                   glob.ts
<anonymous>                         extHostFileSystemEventService.ts
E._deliver / _deliverQueue / fire   event.ts
Dw.$onFileEvent                     extHostFileSystemEventService.ts   ← a file-system event
i._doInvokeHandler / _invokeHandler / _receiveRequest / _receiveOneMessage   rpcProtocol.ts

At the breakpoint:

  • path = the .go file being saved (a path under my workspace)
  • regExp = an 8,691-character regex
  • pattern = the 6,562-character source string it was compiled from
  • the regex contains 459 [/\] separator classes

(The attached sanitized repro reproduces the same shape at 6,512 / 8,628 bytes and 495 separator classes.)

The pattern begins (sanitized — real package names replaced):

'/users/userx/go/src/app/go list failed to return compiledgofiles for "main".build constraints exclude all go files in /users/userx/go/pkg/mod/example.test/x/sys@v0.0.0/windowsmissing go.sum entry for module providing package example.test/module/group/area/section/pkg00 (imported by app/src/component/area/section/pkg00); to add:go get app/src/component/area/section/pkg00 ... (32 such "missing go.sum entry" entries) ...

The compiled regExp begins (sanitized):

/^'[/\]users[/\]userx[/\]go[/\]src[/\]app[/\]go list failed to return compiledgofiles for "main"\.build constraints exclude all go files in [/\]users[/\]userx[/\]go[/\]pkg[/\]mod[/\]example\.test[/\]x[/\]sys@v0\.0\.0[/\]windowsmissing go\.sum entry for module providing package exa ...

Expected behavior

glob.ts should fail safe against pathological / non-glob patterns and never be able to OOM-crash the extension host:

  • Reject or truncate patterns above a sane length (a 6.5 KB watch glob is never legitimate).
  • Cap regex complexity (e.g. number of separator classes / alternations) and fall back to a literal/non-matching matcher when exceeded.
  • Optionally guard regExp.test with a complexity or step budget.

A malformed watch pattern from an extension should at worst log a warning and not match — it must not be able to take down the host.

Actual behavior

glob.ts compiles the 6.5 KB string into an 8,691-char regex; the first regExp.test(path) on a file event causes catastrophic backtracking and OOMs the extension host, silently killing all in-flight extension work.

Attachments

Two files reproduce it deterministically (paths sanitized; structure/size preserved):

  • pattern.txt — the ~6.5 KB non-glob source string (failed go list output)
  • regExp.txt — the ~8.6 KB compiled pathological regex

(Drag these two files into the GitHub issue before submitting. They are sanitized: the local username is userx and all internal package paths are generic placeholders — only the failed-go list structure remains.)

Notes on the upstream trigger (separate issue, FYI only)

The source of the bad pattern is gopls / vscode-go registering a failed go list error string as a watch glob. That is a second, separate bug I may file against golang/vscode-go. This issue is specifically about VS Code core (glob.ts) not being robust against such a pattern — that hardening protects against any extension (Go, .NET, or otherwise) leaking an oversized string into a watch pattern.

Dominant language
TypeScript
Stars
193k
Forks
42.9k
PR merge metrics
PR metrics pending

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.

More from microsoft/vscode

All issues in microsoft/vscode

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.