Skip to content

Commit e99e98c

Browse files
committed
Merge pull request #12786 from tanyifeng:devpts-isig-check
PiperOrigin-RevId: 889359646
2 parents 147cf20 + 0a1922c commit e99e98c

2 files changed

Lines changed: 74 additions & 7 deletions

File tree

‎pkg/sentry/fsimpl/devpts/line_discipline.go‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -494,15 +494,28 @@ func (*inputQueueTransformer) transform(l *lineDiscipline, q *queue, buf []byte)
494494
cBytes[0] = '\r'
495495
}
496496
case l.termios.ControlCharacters[linux.VINTR]: // ctrl-c
497-
// The input queue is reading from the master TTY and
498-
// writing to the replica TTY which is connected to the
499-
// interactive program (like bash). We want to send the
500-
// signal the process connected to the replica TTY.
501-
l.terminal.replicaKTTY.SignalForegroundProcessGroup(kernel.SignalInfoPriv(linux.SIGINT))
497+
if l.termios.LEnabled(linux.ISIG) {
498+
l.terminal.replicaKTTY.SignalForegroundProcessGroup(kernel.SignalInfoPriv(linux.SIGINT))
499+
// In Linux, signal characters are consumed and not passed to
500+
// the reading process. See n_tty.c:n_tty_receive_signal_char().
501+
buf = buf[size:]
502+
ret += size
503+
continue
504+
}
502505
case l.termios.ControlCharacters[linux.VSUSP]: // ctrl-z
503-
l.terminal.replicaKTTY.SignalForegroundProcessGroup(kernel.SignalInfoPriv(linux.SIGTSTP))
506+
if l.termios.LEnabled(linux.ISIG) {
507+
l.terminal.replicaKTTY.SignalForegroundProcessGroup(kernel.SignalInfoPriv(linux.SIGTSTP))
508+
buf = buf[size:]
509+
ret += size
510+
continue
511+
}
504512
case l.termios.ControlCharacters[linux.VQUIT]: // ctrl-\
505-
l.terminal.replicaKTTY.SignalForegroundProcessGroup(kernel.SignalInfoPriv(linux.SIGQUIT))
513+
if l.termios.LEnabled(linux.ISIG) {
514+
l.terminal.replicaKTTY.SignalForegroundProcessGroup(kernel.SignalInfoPriv(linux.SIGQUIT))
515+
buf = buf[size:]
516+
ret += size
517+
continue
518+
}
506519

507520
// In canonical mode, some characters need to be handled specially; for example, backspace.
508521
// This roughly aligns with n_tty.c:n_tty_receive_char_canon and n_tty.c:eraser

‎test/syscalls/linux/pty.cc‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,6 +2159,60 @@ TEST_F(JobControlTest, ReuseControllingTTYAfterExit) {
21592159
ASSERT_NO_ERRNO(res2);
21602160
}
21612161

2162+
// When ISIG is disabled, signal characters (Ctrl-C, Ctrl-Z, Ctrl-\) should
2163+
// be passed through to the reader as ordinary characters, not generate signals.
2164+
// This matches Linux n_tty.c behavior: signal chars are only special when
2165+
// L_ISIG(tty) is true.
2166+
TEST_F(PtyTest, SignalCharPassedThroughWhenISIGDisabled) {
2167+
// Disable ISIG.
2168+
struct kernel_termios t = {};
2169+
EXPECT_THAT(ioctl(replica_.get(), TCGETS, &t), SyscallSucceeds());
2170+
t.c_lflag &= ~ISIG;
2171+
// Also disable ICANON so we can read character by character.
2172+
t.c_lflag &= ~ICANON;
2173+
t.c_cc[VMIN] = 1;
2174+
t.c_cc[VTIME] = 0;
2175+
EXPECT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds());
2176+
2177+
// Write Ctrl-C to master.
2178+
constexpr char kCtrlC = ControlCharacter('C');
2179+
ASSERT_THAT(WriteFd(master_.get(), &kCtrlC, 1), SyscallSucceedsWithValue(1));
2180+
2181+
// The signal character should appear on the replica as a normal byte.
2182+
char buf = 0;
2183+
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), 1));
2184+
ASSERT_THAT(ReadFd(replica_.get(), &buf, 1), SyscallSucceedsWithValue(1));
2185+
EXPECT_EQ(buf, kCtrlC);
2186+
}
2187+
2188+
// When ISIG is enabled (default), signal characters should be consumed and
2189+
// NOT passed to the reading process. In Linux, n_tty_receive_signal_char()
2190+
// calls n_tty_flush_buffer_and_wake() which prevents the character from
2191+
// appearing in the read buffer.
2192+
TEST_F(PtyTest, SignalCharConsumedWhenISIGEnabled) {
2193+
// Ensure ISIG is enabled (it should be by default).
2194+
struct kernel_termios t = {};
2195+
EXPECT_THAT(ioctl(replica_.get(), TCGETS, &t), SyscallSucceeds());
2196+
t.c_lflag |= ISIG;
2197+
// Disable ICANON for raw reading.
2198+
t.c_lflag &= ~ICANON;
2199+
t.c_cc[VMIN] = 1;
2200+
t.c_cc[VTIME] = 0;
2201+
EXPECT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds());
2202+
2203+
// Write Ctrl-C followed by a normal character.
2204+
constexpr char kInput[] = {ControlCharacter('C'), 'a'};
2205+
ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)),
2206+
SyscallSucceedsWithValue(sizeof(kInput)));
2207+
2208+
// Only the normal character 'a' should be readable; Ctrl-C should have been
2209+
// consumed by the signal delivery path.
2210+
char buf = 0;
2211+
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), 1));
2212+
ASSERT_THAT(ReadFd(replica_.get(), &buf, 1), SyscallSucceedsWithValue(1));
2213+
EXPECT_EQ(buf, 'a');
2214+
}
2215+
21622216
} // namespace
21632217
} // namespace testing
21642218
} // namespace gvisor

0 commit comments

Comments
 (0)