Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
133 lines
6.0 KiB
Diff
133 lines
6.0 KiB
Diff
diff --git a/node_modules/node-pty/lib/unixTerminal.js b/node_modules/node-pty/lib/unixTerminal.js
|
|
index af2d24c..a16ab59 100644
|
|
--- a/node_modules/node-pty/lib/unixTerminal.js
|
|
+++ b/node_modules/node-pty/lib/unixTerminal.js
|
|
@@ -65,6 +65,10 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
var parsedEnv = _this._parseEnv(env);
|
|
var encoding = (opt.encoding === undefined ? 'utf8' : opt.encoding);
|
|
var onexit = function (code, signal) {
|
|
+ // Stop queued writes while the PTY fd is still owned by this terminal.
|
|
+ // An async write that outlives the fd can target an unrelated file if
|
|
+ // the operating system reuses the descriptor before libuv runs it.
|
|
+ _this._writeStream.dispose();
|
|
// XXX Sometimes a data event is emitted after exit. Wait til socket is
|
|
// destroyed.
|
|
if (!_this._emittedClose) {
|
|
@@ -105,6 +109,7 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
return;
|
|
}
|
|
}
|
|
+ _this._writeStream.dispose();
|
|
// close
|
|
_this._close();
|
|
// EIO on exit from fs.ReadStream:
|
|
@@ -134,6 +139,7 @@ var UnixTerminal = /** @class */ (function (_super) {
|
|
_this._readable = true;
|
|
_this._writable = true;
|
|
_this._socket.on('close', function () {
|
|
+ _this._writeStream.dispose();
|
|
if (_this._emittedClose) {
|
|
return;
|
|
}
|
|
@@ -288,12 +294,19 @@ var CustomWriteStream = /** @class */ (function () {
|
|
this._fd = _fd;
|
|
this._encoding = _encoding;
|
|
this._writeQueue = [];
|
|
+ this._disposed = false;
|
|
+ this._fdIdentity = fs.fstatSync(_fd);
|
|
}
|
|
CustomWriteStream.prototype.dispose = function () {
|
|
+ this._disposed = true;
|
|
clearImmediate(this._writeImmediate);
|
|
this._writeImmediate = undefined;
|
|
+ this._writeQueue.length = 0;
|
|
};
|
|
CustomWriteStream.prototype.write = function (data) {
|
|
+ if (this._disposed) {
|
|
+ return;
|
|
+ }
|
|
// Writes are put in a queue and processed asynchronously in order to handle
|
|
// backpressure from the kernel buffer.
|
|
var buffer = typeof data === 'string'
|
|
@@ -309,42 +322,53 @@ var CustomWriteStream = /** @class */ (function () {
|
|
CustomWriteStream.prototype._processWriteQueue = function () {
|
|
var _this = this;
|
|
this._writeImmediate = undefined;
|
|
- if (this._writeQueue.length === 0) {
|
|
+ if (this._disposed || this._writeQueue.length === 0) {
|
|
return;
|
|
}
|
|
var task = this._writeQueue[0];
|
|
+ if (!this._ownsFileDescriptor()) {
|
|
+ this.dispose();
|
|
+ return;
|
|
+ }
|
|
// Write to the underlying file descriptor and handle it directly, rather
|
|
// than using the `net.Socket`/`tty.WriteStream` wrappers which swallow and
|
|
// mask errors like EAGAIN and can cause the thread to block indefinitely.
|
|
- fs.write(this._fd, task.buffer, task.offset, function (err, written) {
|
|
- if (err) {
|
|
- if ('code' in err && err.code === 'EAGAIN') {
|
|
- // `setImmediate` is used to yield to the event loop and re-attempt
|
|
- // the write later.
|
|
- _this._writeImmediate = setImmediate(function () { return _this._processWriteQueue(); });
|
|
- }
|
|
- else {
|
|
- // Stop processing immediately on unexpected error and log
|
|
- _this._writeQueue.length = 0;
|
|
- console.error('Unhandled pty write error', err);
|
|
- }
|
|
- return;
|
|
- }
|
|
+ // The native layer marks the PTY master fd as non-blocking. Keep each raw-fd
|
|
+ // write synchronous so no libuv request can outlive the descriptor, then
|
|
+ // yield between writes to retain asynchronous queue and backpressure behavior.
|
|
+ try {
|
|
+ var written = fs.writeSync(this._fd, task.buffer, task.offset);
|
|
task.offset += written;
|
|
if (task.offset >= task.buffer.byteLength) {
|
|
- _this._writeQueue.shift();
|
|
+ this._writeQueue.shift();
|
|
}
|
|
- // Since there is more room in the kernel buffer, we can continue to write
|
|
- // until we hit EAGAIN or exhaust the queue.
|
|
- //
|
|
- // Note that old versions of bash, like v3.2 which ships in macOS, appears
|
|
- // to have a bug in its readline implementation that causes data
|
|
- // corruption when writes to the pty happens too quickly. Instead of
|
|
- // trying to workaround that we just accept it so that large pastes are as
|
|
- // fast as possible.
|
|
- // Context: https://github.com/microsoft/node-pty/issues/833
|
|
- _this._processWriteQueue();
|
|
- });
|
|
+ }
|
|
+ catch (err) {
|
|
+ if (typeof err !== 'object' || err === null || !('code' in err) || err.code !== 'EAGAIN') {
|
|
+ // Stop processing immediately on unexpected error and log
|
|
+ this._writeQueue.length = 0;
|
|
+ console.error('Unhandled pty write error', err);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ if (!this._disposed && this._writeQueue.length > 0) {
|
|
+ this._writeImmediate = setImmediate(function () { return _this._processWriteQueue(); });
|
|
+ }
|
|
+ };
|
|
+ CustomWriteStream.prototype._ownsFileDescriptor = function () {
|
|
+ try {
|
|
+ var current = fs.fstatSync(this._fd);
|
|
+ return current.dev === this._fdIdentity.dev
|
|
+ && current.ino === this._fdIdentity.ino
|
|
+ && current.mode === this._fdIdentity.mode
|
|
+ && current.rdev === this._fdIdentity.rdev;
|
|
+ }
|
|
+ catch (err) {
|
|
+ if (typeof err === 'object' && err !== null && 'code' in err && err.code === 'EBADF') {
|
|
+ return false;
|
|
+ }
|
|
+ throw err;
|
|
+ }
|
|
};
|
|
return CustomWriteStream;
|
|
}());
|