diff --git a/pkg/sentry/fsimpl/devpts/line_discipline.go b/pkg/sentry/fsimpl/devpts/line_discipline.go index 714554c8c..26ba4d85a 100644 --- a/pkg/sentry/fsimpl/devpts/line_discipline.go +++ b/pkg/sentry/fsimpl/devpts/line_discipline.go @@ -154,7 +154,7 @@ func (l *lineDiscipline) setTermios(task *kernel.Task, args arch.SyscallArgument if oldCanonEnabled && !l.termios.LEnabled(linux.ICANON) { l.inQueue.mu.Lock() l.inQueue.pushWaitBufLocked(l) - l.inQueue.readable = true + l.inQueue.readable = len(l.inQueue.readBuf) > 0 l.inQueue.mu.Unlock() l.termiosMu.Unlock() l.replicaWaiter.Notify(waiter.ReadableEvents) diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc index 1a75e587c..1cee2a551 100644 --- a/test/syscalls/linux/pty.cc +++ b/test/syscalls/linux/pty.cc @@ -1152,6 +1152,23 @@ TEST_F(PtyTest, SwitchNoncanonToCanonNoNewlineBig) { ExpectFinished(replica_); } +// If the canonical input buffer is empty, and we switch to non-canonical +// mode, the input buffer should not be readable. +TEST_F(PtyTest, SwitchCanonToNoncanonEmptyInput) { + constexpr char kInput[] = ""; + ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)), + SyscallSucceedsWithValue(sizeof(kInput))); + + DisableCanonical(); + + // Nothing available yet. + char buf[2] = {}; + ASSERT_THAT(PollAndReadFd(replica_.get(), buf, 2, kTimeoutShort), + PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); + + ExpectFinished(replica_); +} + // Tests that we can write over the 4095 noncanonical limit, then read out // everything. TEST_F(PtyTest, NoncanonBigWrite) {