Skip to content

Instantly share code, notes, and snippets.

@tarunlalwani
Last active July 10, 2026 16:48
Show Gist options
  • Select an option

  • Save tarunlalwani/82836a7b175d05e8365dace824b3c73b to your computer and use it in GitHub Desktop.

Select an option

Save tarunlalwani/82836a7b175d05e8365dace824b3c73b to your computer and use it in GitHub Desktop.
Electron CDP Fetch.enable + failed requests → native crash (RequestBodyCollector / InterceptionJob::OnComplete UAF)

Electron CDP Fetch crash repro

Native browser-process crash when Fetch.enable (Response) is active and many requests fail (connection refused) while the OS still reports online.

Stack (symbolicated)

content::RequestBodyCollector::~RequestBodyCollector()
content::InterceptionJob::NotifyClient(...)
content::InterceptionJob::OnComplete(...)  // devtools_url_loader_interceptor.cc

Run

npm i
npx electron .

Or paste main.js into Electron Fiddle (Electron 43.x).

Success

Process dies with Crashpad / EXC_BAD_ACCESS (macOS) or ACCESS_VIOLATION (Windows). If you see STILL_ALIVE / exit code 2, increase FAIL_COUNT / WAVE_COUNT.

Related

  • Chromium 350029229 (RequestBodyCollector, different crash site)
  • Electron #46109 (different Fetch bug; fixed)

Shareable gist

https://gist.github.com/tarunlalwani/82836a7b175d05e8365dace824b3c73b

Notes

  • Reproduced as native crash in TimeBack on macOS with Pi-hole / mass failed requests + CDP Fetch.
  • Minimal Electron 43.1.0 on Linux (Xvfb) survived 1200 GET and 1000 POST connection-refused continues — try on macOS / with real blocked hosts.
  • Symbolicated stack: RequestBodyCollector dtor ← InterceptionJob::NotifyClient ← OnComplete.
const { app, BrowserWindow, crashReporter } = require('electron');
const FAIL_COUNT = 100;
const WAVE_COUNT = 10;
const WAVE_GAP_MS = 100;
const CLOSED_PORT = 59999;
crashReporter.start({
productName: 'electron-fetch-crash-repro',
companyName: 'repro',
submitURL: '',
uploadToServer: false,
});
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
app.whenReady().then(async () => {
console.log('ready pid=', process.pid);
const win = new BrowserWindow({
width: 800, height: 600, show: true,
webPreferences: { nodeIntegration: false, contextIsolation: true },
});
const wc = win.webContents;
await wc.loadURL('about:blank');
const dbg = wc.debugger;
dbg.attach('1.3');
let paused = 0, continued = 0;
dbg.on('message', async (_e, method, params) => {
if (method !== 'Fetch.requestPaused') return;
paused++;
const { requestId } = params;
try {
try { await dbg.sendCommand('Fetch.getResponseBody', { requestId }); } catch (_) {}
await dbg.sendCommand('Fetch.continueResponse', { requestId });
continued++;
} catch (_) {
try { await dbg.sendCommand('Fetch.continueRequest', { requestId }); continued++; } catch (_) {}
}
});
await dbg.sendCommand('Network.enable');
await dbg.sendCommand('Fetch.enable', {
patterns: [
{ requestStage: 'Response', resourceType: 'Fetch' },
{ requestStage: 'Response', resourceType: 'XHR' },
{ requestStage: 'Response' },
],
});
console.log('Fetch.enable active');
// POST with body → RequestBodyCollector path (matches crash stack)
const html = `<!doctype html><body>go</body><script>
(async () => {
const body = new Uint8Array(64 * 1024); // 64KiB
for (let i = 0; i < body.length; i++) body[i] = i & 255;
for (let w = 0; w < ${WAVE_COUNT}; w++) {
await Promise.allSettled(Array.from({length:${FAIL_COUNT}}, (_, i) =>
fetch('http://127.0.0.1:${CLOSED_PORT}/w'+w+'-'+i, {
method: 'POST',
headers: { 'content-type': 'application/octet-stream' },
body,
}).catch(() => null)
));
document.body.textContent = 'wave '+(w+1);
await new Promise(r => setTimeout(r, ${WAVE_GAP_MS}));
}
document.body.textContent = 'done-no-crash';
})();
</script>`;
await wc.loadURL('data:text/html;charset=utf-8,' + encodeURIComponent(html));
console.log('fail page loaded (POST bodies)');
await sleep(WAVE_COUNT * WAVE_GAP_MS + 25000);
console.log('STILL_ALIVE paused=', paused, 'continued=', continued);
app.exit(2);
});
{
"name": "electron-fetch-crash-repro",
"private": true,
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "43.1.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment