summaryrefslogtreecommitdiffhomepage
path: root/pkg
diff options
context:
space:
mode:
authorAndrei Vagin <avagin@google.com>2021-01-19 15:31:49 -0800
committergVisor bot <gvisor-bot@google.com>2021-01-19 15:34:02 -0800
commit48ea2c34d1d3dead7727d9e2760b587c7609b14b (patch)
tree7b8e2c8bc3a2a1a8da7f780800e4d2aae674b943 /pkg
parentbe17b94446b2f96c2a3d531fe20271537c77c8aa (diff)
platform/ptrace: workaround a kernel ptrace issue on ARM64
On ARM64, when ptrace stops on a system call, it uses the x7 register to indicate whether the stop has been signalled from syscall entry or syscall exit. This means that we can't get a value of this register and we can't change it. More details are in the comment for tracehook_report_syscall in arch/arm64/kernel/ptrace.c. This happens only if we stop on a system call, so let's queue a signal, resume a stub thread and catch it on a signal handling. Fixes: #5238 PiperOrigin-RevId: 352668695
Diffstat (limited to 'pkg')
-rw-r--r--pkg/sentry/platform/ptrace/subprocess.go12
-rw-r--r--pkg/sentry/platform/ptrace/subprocess_amd64.go3
-rw-r--r--pkg/sentry/platform/ptrace/subprocess_arm64.go36
3 files changed, 47 insertions, 4 deletions
diff --git a/pkg/sentry/platform/ptrace/subprocess.go b/pkg/sentry/platform/ptrace/subprocess.go
index aacd7ce70..17fb0a0d8 100644
--- a/pkg/sentry/platform/ptrace/subprocess.go
+++ b/pkg/sentry/platform/ptrace/subprocess.go
@@ -550,6 +550,12 @@ func (s *subprocess) switchToApp(c *context, ac arch.Context) bool {
// Wait for the syscall-enter stop.
sig := t.wait(stopped)
+ if sig == syscall.SIGSTOP {
+ // SIGSTOP was delivered to another thread in the same thread
+ // group, which initiated another group stop. Just ignore it.
+ continue
+ }
+
// Refresh all registers.
if err := t.getRegs(regs); err != nil {
panic(fmt.Sprintf("ptrace get regs failed: %v", err))
@@ -566,13 +572,11 @@ func (s *subprocess) switchToApp(c *context, ac arch.Context) bool {
// Is it a system call?
if sig == (syscallEvent | syscall.SIGTRAP) {
+ s.arm64SyscallWorkaround(t, regs)
+
// Ensure registers are sane.
updateSyscallRegs(regs)
return true
- } else if sig == syscall.SIGSTOP {
- // SIGSTOP was delivered to another thread in the same thread
- // group, which initiated another group stop. Just ignore it.
- continue
}
// Grab signal information.
diff --git a/pkg/sentry/platform/ptrace/subprocess_amd64.go b/pkg/sentry/platform/ptrace/subprocess_amd64.go
index 020bbda79..04815282b 100644
--- a/pkg/sentry/platform/ptrace/subprocess_amd64.go
+++ b/pkg/sentry/platform/ptrace/subprocess_amd64.go
@@ -257,3 +257,6 @@ func probeSeccomp() bool {
}
}
}
+
+func (s *subprocess) arm64SyscallWorkaround(t *thread, regs *arch.Registers) {
+}
diff --git a/pkg/sentry/platform/ptrace/subprocess_arm64.go b/pkg/sentry/platform/ptrace/subprocess_arm64.go
index bd618fae8..416132967 100644
--- a/pkg/sentry/platform/ptrace/subprocess_arm64.go
+++ b/pkg/sentry/platform/ptrace/subprocess_arm64.go
@@ -21,6 +21,7 @@ import (
"strings"
"syscall"
+ "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/seccomp"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -172,3 +173,38 @@ func appendArchSeccompRules(rules []seccomp.RuleSet, defaultAction linux.BPFActi
func probeSeccomp() bool {
return true
}
+
+func (s *subprocess) arm64SyscallWorkaround(t *thread, regs *arch.Registers) {
+ // On ARM64, when ptrace stops on a system call, it uses the x7
+ // register to indicate whether the stop has been signalled from
+ // syscall entry or syscall exit. This means that we can't get a value
+ // of this register and we can't change it. More details are in the
+ // comment for tracehook_report_syscall in arch/arm64/kernel/ptrace.c.
+ //
+ // This happens only if we stop on a system call, so let's queue a
+ // signal, resume a stub thread and catch it on a signal handling.
+ t.NotifyInterrupt()
+ for {
+ if _, _, errno := syscall.RawSyscall6(
+ syscall.SYS_PTRACE,
+ unix.PTRACE_SYSEMU,
+ uintptr(t.tid), 0, 0, 0, 0); errno != 0 {
+ panic(fmt.Sprintf("ptrace sysemu failed: %v", errno))
+ }
+
+ // Wait for the syscall-enter stop.
+ sig := t.wait(stopped)
+ if sig == syscall.SIGSTOP {
+ // SIGSTOP was delivered to another thread in the same thread
+ // group, which initiated another group stop. Just ignore it.
+ continue
+ }
+ if sig == (syscallEvent | syscall.SIGTRAP) {
+ t.dumpAndPanic(fmt.Sprintf("unexpected syscall event"))
+ }
+ break
+ }
+ if err := t.getRegs(regs); err != nil {
+ panic(fmt.Sprintf("ptrace get regs failed: %v", err))
+ }
+}