fix(core): fix PTY descriptor shell leak (#16773)

This commit is contained in:
Gal Zahavi
2026-01-16 09:55:29 -08:00
committed by GitHub
parent be37c26c88
commit 013a4e02ff
4 changed files with 171 additions and 5 deletions

View File

@@ -12,7 +12,11 @@ import { TextDecoder } from 'node:util';
import os from 'node:os';
import type { IPty } from '@lydell/node-pty';
import { getCachedEncodingForBuffer } from '../utils/systemEncoding.js';
import { getShellConfiguration, type ShellType } from '../utils/shell-utils.js';
import {
getShellConfiguration,
resolveExecutable,
type ShellType,
} from '../utils/shell-utils.js';
import { isBinary } from '../utils/textUtils.js';
import pkg from '@xterm/headless';
import {
@@ -183,7 +187,7 @@ export class ShellExecutionService {
const ptyInfo = await getPty();
if (ptyInfo) {
try {
return this.executeWithPty(
return await this.executeWithPty(
commandToExecute,
cwd,
onOutputEvent,
@@ -445,14 +449,14 @@ export class ShellExecutionService {
}
}
private static executeWithPty(
private static async executeWithPty(
commandToExecute: string,
cwd: string,
onOutputEvent: (event: ShellOutputEvent) => void,
abortSignal: AbortSignal,
shellExecutionConfig: ShellExecutionConfig,
ptyInfo: PtyImplementation,
): ShellExecutionHandle {
): Promise<ShellExecutionHandle> {
if (!ptyInfo) {
// This should not happen, but as a safeguard...
throw new Error('PTY implementation not found');
@@ -461,6 +465,14 @@ export class ShellExecutionService {
const cols = shellExecutionConfig.terminalWidth ?? 80;
const rows = shellExecutionConfig.terminalHeight ?? 30;
const { executable, argsPrefix, shell } = getShellConfiguration();
const resolvedExecutable = await resolveExecutable(executable);
if (!resolvedExecutable) {
throw new Error(
`Shell executable "${executable}" not found in PATH or at absolute location. Please ensure the shell is installed and available in your environment.`,
);
}
const guardedCommand = ensurePromptvarsDisabled(commandToExecute, shell);
const args = [...argsPrefix, guardedCommand];
@@ -660,6 +672,12 @@ export class ShellExecutionService {
exited = true;
abortSignal.removeEventListener('abort', abortHandler);
this.activePtys.delete(ptyProcess.pid);
// Attempt to destroy the PTY to ensure FD is closed
try {
(ptyProcess as IPty & { destroy?: () => void }).destroy?.();
} catch {
// Ignore errors during cleanup
}
const finalize = () => {
render(true);