diff --git a/pkg/sentry/kernel/thread_group.go b/pkg/sentry/kernel/thread_group.go index 77953ad41..5475ff66b 100644 --- a/pkg/sentry/kernel/thread_group.go +++ b/pkg/sentry/kernel/thread_group.go @@ -514,9 +514,10 @@ func (tg *ThreadGroup) SetForegroundProcessGroup(tty *TTY, pgid ProcessGroupID) // signal is sent to all members of this background process group. // We need also need to check whether it is ignoring or blocking SIGTTOU. ignored := signalAction.Handler == linux.SIG_IGN - blocked := linux.SignalSet(tg.leader.signalMask.RacyLoad()) == linux.SignalSetOf(linux.SIGTTOU) + blocked := (linux.SignalSet(tg.leader.signalMask.RacyLoad()) & linux.SignalSetOf(linux.SIGTTOU)) != 0 if tg.processGroup.id != tg.processGroup.session.foreground.id && !ignored && !blocked { tg.leader.sendSignalLocked(SignalInfoPriv(linux.SIGTTOU), true) + return -1, linuxerr.ERESTARTSYS } tg.processGroup.session.foreground.id = pgid diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc index f93eb7c77..3d8ae1ca3 100644 --- a/test/syscalls/linux/pty.cc +++ b/test/syscalls/linux/pty.cc @@ -1656,6 +1656,10 @@ TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUBackground) { int wstatus; TEST_PCHECK(waitpid(grandchild, &wstatus, WSTOPPED) == grandchild); TEST_PCHECK(WSTOPSIG(wstatus) == SIGTTOU); + + // The child's `tcsetpgrp` got signalled and so should not have + // taken effect. Verify that. + TEST_PCHECK(tcgetpgrp(replica_.get()) == getpid()); EXPECT_THAT(kill(grandchild, SIGKILL), SyscallSucceeds()); }); ASSERT_NO_ERRNO(res); @@ -1703,11 +1707,20 @@ TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUBlocked) { sigset_t signal_set; sigemptyset(&signal_set); sigaddset(&signal_set, SIGTTOU); + // Block SIGTTIN as well, to make sure that the kernel isn't + // checking for "blocked == [SIGTTOU]" (see issue 7941 for + // context). + sigaddset(&signal_set, SIGTTIN); sigprocmask(SIG_BLOCK, &signal_set, NULL); // Assign a different pgid to the child so it will result as // a background process. TEST_PCHECK(!setpgid(grandchild, getpid())); TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0))); + // Unmask the signals to make sure we still don't get + // signaled. That would happen if `tcsetpgrp` enqueued the + // signal through the mask -- we would not yet have received it, + // because of the mask. + sigprocmask(SIG_UNBLOCK, &signal_set, NULL); _exit(0); } int wstatus;