OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git commits / 08923cb

Fix git-remote-punch: prevent zombie processes and double-free crash
Two bugs from premature process.exit() during async Hyperswarm teardown: 1. Zombie processes: process.exit() during swarm.destroy() leaves io_uring operations in flight. The process never fully exits, spinning at 99% CPU. On stortron, 10 zombies saturated the machine, blocking all new P2P connections. 2. Double-free crash: native libudx buffers freed by process.exit() while io_uring still references them. Manifests as "double free or corruption" or "invalid pointer" on process teardown. Fix: await connector.destroy() fully before process.exit(). Add 5s hard-kill timeout so a hung teardown doesn't block forever. Applied to both the timeout catch path and the successful cleanup function. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
08923cb794e3537931f2ca04f2bd9ed9e454be93
by plankatron <fleeky@gmail.com> · 2026-04-07T13:36:25.000Z

Changed files (1) +16 -3

bin/git-remote-punch +16 -3
……@@ -169,9 +169,14 @@ async function handleConnect(service, serverKey, repoName, httpsUrl, stdout, std
169169 stdout.write('\n')
170170
171171 function cleanup (code) {
172 // Hard kill safety — if async teardown hangs, don't spin forever
173 const hardKill = setTimeout(() => process.exit(code), 5000)
174 hardKill.unref()
172175 try { conn.destroy() } catch {}
173 try { connector.destroy() } catch {}
174 process.exit(code)
176 connector.destroy().catch(() => {}).finally(() => {
177 clearTimeout(hardKill)
178 process.exit(code)
179 })
175180 }
176181
177182 // Manual bidirectional forwarding — NOT pipe()!
……@@ -204,7 +209,15 @@ async function handleConnect(service, serverKey, repoName, httpsUrl, stdout, std
204209
205210 } catch (err) {
206211 stderr.write(`[punch] P2P connection failed: ${err.message}\n`)
207 await connector.destroy()
212
213 // Destroy swarm fully before exiting — premature process.exit()
214 // during async Hyperswarm teardown causes double-free crashes
215 // (libudx/io_uring buffers freed while still in use) and zombie
216 // processes that spin at 100% CPU.
217 const hardKill = setTimeout(() => process.exit(128), 5000)
218 hardKill.unref()
219 try { await connector.destroy() } catch {}
220 clearTimeout(hardKill)
208221
209222 if (httpsUrl) {
210223 stderr.write(`[punch] Falling back to HTTPS: ${httpsUrl}\n`)