[pty] Return enqueued data and then EIO if the replica is closed

PiperOrigin-RevId: 532496868
This commit is contained in:
gVisor bot
2023-05-16 10:15:01 -07:00
parent 595d424651
commit 243aeddd59
4 changed files with 47 additions and 1 deletions
@@ -106,6 +106,9 @@ type lineDiscipline struct {
// handling certain special characters like backspace.
column int
// numReplicas is the number of replica file descriptors.
numReplicas int
// masterWaiter is used to wait on the master end of the TTY.
masterWaiter waiter.Queue
@@ -269,6 +272,20 @@ func (l *lineDiscipline) outputQueueWrite(ctx context.Context, src usermem.IOSeq
return 0, linuxerr.ErrWouldBlock
}
// replicaOpen is called when a replica file descriptor is opened.
func (l *lineDiscipline) replicaOpen() {
l.termiosMu.Lock()
defer l.termiosMu.Unlock()
l.numReplicas++
}
// replicaClose is called when a replica file descriptor is closed.
func (l *lineDiscipline) replicaClose() {
l.termiosMu.Lock()
defer l.termiosMu.Unlock()
l.numReplicas--
}
// transformer is a helper interface to make it easier to stateify queue.
type transformer interface {
// transform functions require queue's mutex to be held.
+3
View File
@@ -110,6 +110,9 @@ func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipl
defer q.mu.Unlock()
if !q.readable {
if l.numReplicas == 0 {
return 0, false, false, linuxerr.EIO
}
return 0, false, false, linuxerr.ErrWouldBlock
}
+4 -1
View File
@@ -69,6 +69,7 @@ func (ri *replicaInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kern
// ignored silently.
_ = t.ThreadGroup().SetControllingTTY(fd.inode.t.replicaKTTY, false /* steal */, fd.vfsfd.IsReadable())
}
ri.t.ld.replicaOpen()
return &fd.vfsfd, nil
}
@@ -114,7 +115,9 @@ type replicaFileDescription struct {
var _ vfs.FileDescriptionImpl = (*replicaFileDescription)(nil)
// Release implements fs.FileOperations.Release.
func (rfd *replicaFileDescription) Release(ctx context.Context) {}
func (rfd *replicaFileDescription) Release(ctx context.Context) {
rfd.inode.t.ld.replicaClose()
}
// EventRegister implements waiter.Waitable.EventRegister.
func (rfd *replicaFileDescription) EventRegister(e *waiter.Entry) error {
+23
View File
@@ -650,6 +650,29 @@ TEST_F(PtyTest, WriteReplicaToMaster) {
EXPECT_EQ(memcmp(buf, kExpected, sizeof(kExpected)), 0);
}
// Verifies that data enqueued into the replica is still readable by the master
// after the replica is closed.
TEST_F(PtyTest, WriteReplicaToMasterReadAfterReplicaClosed) {
// N.B. by default, the master reads nothing until the replica writes a
// newline, and the master gets a carriage return.
constexpr char kInput[] = "hello\n";
constexpr char kExpected[] = "hello\r\n";
EXPECT_THAT(WriteFd(replica_.get(), kInput, sizeof(kInput) - 1),
SyscallSucceedsWithValue(sizeof(kInput) - 1));
// Close the replica.
replica_.reset();
char buf[sizeof(kExpected)] = {};
ExpectReadable(master_, sizeof(buf) - 1, buf);
EXPECT_EQ(memcmp(buf, kExpected, sizeof(kExpected)), 0);
// After all data has been read, the master should return EIO.
char c;
EXPECT_THAT(ReadFd(master_.get(), &c, 1), SyscallFailsWithErrno(EIO));
}
TEST_F(PtyTest, WriteInvalidUTF8) {
char c = 0xff;
ASSERT_THAT(syscall(__NR_write, master_.get(), &c, sizeof(c)),