diff --git a/pkg/sentry/platform/systrap/shared_context.go b/pkg/sentry/platform/systrap/shared_context.go index a3e900aec0..86ef064aba 100644 --- a/pkg/sentry/platform/systrap/shared_context.go +++ b/pkg/sentry/platform/systrap/shared_context.go @@ -129,11 +129,12 @@ func (sc *sharedContext) NotifyInterrupt() { if sc.threadID() == invalidThreadID { return } - sc.subprocess.sysmsgThreadsMu.Lock() - defer sc.subprocess.sysmsgThreadsMu.Unlock() + s := sc.subprocess + s.sysmsgThreadsMu.Lock() + defer s.sysmsgThreadsMu.Unlock() threadID := atomic.LoadUint32(&sc.shared.ThreadID) - sysmsgThread, ok := sc.subprocess.sysmsgThreads[threadID] + sysmsgThread, ok := s.sysmsgThreads[threadID] if !ok { // This is either an invalidThreadID or another garbage value; either way we // don't know which thread to interrupt; best we can do is mark the context. @@ -142,7 +143,18 @@ func (sc *sharedContext) NotifyInterrupt() { t := sysmsgThread.thread if e := hostsyscall.RawSyscallErrno(unix.SYS_TGKILL, uintptr(t.tgid), uintptr(t.tid), uintptr(platform.SignalInterrupt)); e != 0 { - panic(fmt.Sprintf("failed to interrupt the child process %d: %v", t.tid, e)) + if e == unix.ESRCH { // Stub thread already killed? + s.dead.Store(true) + if !sc.shared.State.CompareAndSwap(sysmsg.ContextStateNone, sysmsg.ContextStateUnexpectedDeath) { + s.syscallThread.thread.Warningf("failed to set context state to ContextStateUnexpectedDeath; context state was %v", sc.state()) + } + s.syscallThreadMu.Lock() + defer s.syscallThreadMu.Unlock() + s.syscallThread.thread.Warningf("Cannot interrupt stub thread %sas it no longer exists; killing syscall thread.", *t.loadLogPrefix()) + s.syscallThread.thread.kill() + } else { + panic(fmt.Sprintf("failed to interrupt the child process %d: %v", t.tid, e)) + } } } diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index a3182b73c0..e9d5b63265 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -685,7 +685,7 @@ func (t *thread) wait(outcome waitOutcome) unix.Signal { } } -// kill kills the thread; +// kill kills the thread. func (t *thread) kill() { unix.Tgkill(int(t.tgid), int(t.tid), unix.Signal(unix.SIGKILL)) } @@ -863,13 +863,17 @@ func (s *subprocess) switchToApp(c *platformContext, ac *arch.Context64) (isSysc ctxState = sysmsg.ContextStateSyscall shouldPatchSyscall = true } - if ctxState == sysmsg.ContextStateSyscall || ctxState == sysmsg.ContextStateSyscallTrap { if maybePatchSignalInfo(regs, &c.signalInfo) { return false, false, hostarch.Execute, nil } updateSyscallRegs(regs) return true, shouldPatchSyscall, hostarch.NoAccess, nil + } else if ctxState == sysmsg.ContextStateUnexpectedDeath { + return false, shouldPatchSyscall, hostarch.NoAccess, &platform.ContextError{ + Err: fmt.Errorf("systrap unexpected death"), + Errno: unix.ECHILD, + } } else if ctxState != sysmsg.ContextStateFault { return false, false, hostarch.NoAccess, corruptedSharedMemoryErr(fmt.Sprintf("unknown context state: %v", ctxState)) } diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go index 60f1796ac4..9b61ab3b9a 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.go +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.go @@ -172,7 +172,7 @@ type Msg struct { // or ContextStateNone if running/ready-to-run. type ContextState uint32 -// Set atomicaly sets the state value. +// Set atomically sets the state value. func (s *ContextState) Set(state ContextState) { atomic.StoreUint32((*uint32)(s), uint32(state)) } @@ -184,6 +184,11 @@ func (s *ContextState) Get() ContextState { return ContextState(atomic.LoadUint32((*uint32)(s))) } +// CompareAndSwap performs a compare-and-swap operation on the state value. +func (s *ContextState) CompareAndSwap(old, new ContextState) bool { + return atomic.CompareAndSwapUint32((*uint32)(s), uint32(old), uint32(new)) +} + // Context State types. const ( // ContextStateNone means that is either running in the user task or is ready @@ -201,6 +206,8 @@ const ( // ContextStateSyscallCanBePatched means that the syscall can be replaced // with a function call. ContextStateSyscallCanBePatched + // ContextStateUnexpectedDeath means a stub thread died unexpectedly. + ContextStateUnexpectedDeath // ContextStateInvalid is an invalid state that the sentry should never see. ContextStateInvalid ) diff --git a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h index 056d2d1e0d..86b98fb117 100644 --- a/pkg/sentry/platform/systrap/sysmsg/sysmsg.h +++ b/pkg/sentry/platform/systrap/sysmsg/sysmsg.h @@ -75,6 +75,7 @@ enum context_state { CONTEXT_STATE_FAULT, CONTEXT_STATE_SYSCALL_TRAP, CONTEXT_STATE_SYSCALL_NEED_TRAP, + CONTEXT_STATE_UNEXPECTED_DEATH, CONTEXT_STATE_INVALID, };