inspector: Runtime.evaluate with the contextId of a just-collected vm context segfaults

Ouverte
#67,138 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

Personne n'a encore pris cette issue.

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
42/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Active
Stack technique
cpp, javascript, node.js
Domaine
devtools

Piste de recherche

Run the target.js and client.mjs reproduction first; it should currently exit with SIGSEGV when Runtime.evaluate targets a collected context. Read the cited V8 src/inspector/inspected-context.cc path and the backtrace through InjectedScript::Scope::initialize; the report notes the fix may belong in V8. Done means the target reliably finishes and evaluating a missing context returns “Cannot find context with specified id.”

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

Version

v22.23.4, v24.22.2, v26.13.0 with the repro below; v24.21.0 in the backtrace further down

Platform

Linux x64 (Ubuntu 22.04 container and Debian bookworm node images), Linux arm64 (node:26-slim)

Subsystem

inspector

What steps will reproduce the bug?

target.js:

const vm = require('node:vm');
let n = 0;
function step() {
  vm.runInNewContext('1');
  let junk = [];
  const t = Date.now();
  while (Date.now() - t < 3) {
    junk.push({ n });
    if (junk.length > 1e5) junk = [];
  }
  if (++n < 2000) setImmediate(step);
  else console.log('finished', n);
}
step();

client.mjs, in the same directory:

import { spawn } from 'node:child_process';

const child = spawn(process.execPath, ['--inspect-brk=127.0.0.1:0', new URL('target.js', import.meta.url).pathname], { stdio: ['ignore', 'inherit', 'pipe'] });
let ws;
child.stderr.on('data', (d) => {
  const url = String(d).match(/ws:\/\/\S+/);
  if (url && !ws) connect(url[0]);
  if (/Waiting for the debugger to disconnect/.test(d)) ws.close();
});
child.on('exit', (code, signal) => console.log(`target exited: code=${code} signal=${signal}`));

function connect(url) {
  ws = new WebSocket(url);
  let id = 0;
  const send = (method, params = {}) => ws.send(JSON.stringify({ id: ++id, method, params }));
  ws.onmessage = ({ data }) => {
    const msg = JSON.parse(data);
    if (msg.method === 'Runtime.executionContextCreated' && msg.params.context.id > 1) {
      send('Runtime.evaluate', { expression: '1', contextId: msg.params.context.id });
    }
  };
  ws.onopen = () => {
    send('Runtime.enable');
    send('Runtime.runIfWaitingForDebugger');
  };
}

Run node client.mjs.

How often does it reproduce? Is there a required condition?

Most runs on Linux: 14 of 15 on x64 (Docker on Apple Silicon) across 22, 24 and 26, and 9 of 10 on arm64 with 26.13.0. Without the Runtime.evaluate it always finishes. It didn't reproduce on macOS arm64 with v22.19.0 in 2 runs.

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

finished 2000 and exit code 0. An evaluate in a context that no longer exists should fail with "Cannot find context with specified id".

What do you see instead?

target exited: code=null signal=SIGSEGV.

Additional information

Backtrace from a real crash (v24.21.0, x64):

#0  v8::Context::Enter()
#1  v8_inspector::InjectedScript::Scope::initialize()
#2  v8_inspector::V8RuntimeAgentImpl::evaluate(...)
...
#9  node::inspector::MainThreadInterface::DispatchMessages()
#14 v8::internal::StackGuard::HandleInterrupts(...)

The context pointer is null. In V8's src/inspector/inspected-context.cc (main today), the weak callback resetContext empties the context handle and posts a foreground task to discard the context. Until that task runs, session->findInjectedScript still finds the context id, and InjectedScript::Scope::initialize calls m_injectedScript->context()->context()->Enter() without checking for an empty handle. An evaluate dispatched by interrupt in that window crashes. If that's right the fix belongs in V8; happy to file it there instead.

We hit it through VS Code's JavaScript debugger, which evaluates a hook in every new context once a breakpoint is set, while require('repl') (pulled in by ts-node) creates a throwaway context: (js-debug issue link).

Langage dominant
JavaScript
Étoiles
123k
Forks
39.3k
Merge moyen
4 j 16 h
PR mergées (30 j)
227

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de nodejs/node

Toutes les issues de nodejs/node

Issues similaires

Plus d'issues JavaScript

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.