Implement rseq(2)

PiperOrigin-RevId: 288342928
This commit is contained in:
Michael Pratt
2020-01-06 11:42:44 -08:00
committed by gVisor bot
parent 6410387ff9
commit 354a15a234
15 changed files with 603 additions and 84 deletions
+1
View File
@@ -41,6 +41,7 @@ go_library(
"poll.go",
"prctl.go",
"ptrace.go",
"rseq.go",
"rusage.go",
"sched.go",
"seccomp.go",
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linux
// Flags passed to rseq(2).
//
// Defined in include/uapi/linux/rseq.h.
const (
// RSEQ_FLAG_UNREGISTER unregisters the current thread.
RSEQ_FLAG_UNREGISTER = 1 << 0
)
// Critical section flags used in RSeqCriticalSection.Flags and RSeq.Flags.
//
// Defined in include/uapi/linux/rseq.h.
const (
// RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT inhibits restart on preemption.
RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT = 1 << 0
// RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL inhibits restart on signal
// delivery.
RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL = 1 << 1
// RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE inhibits restart on CPU
// migration.
RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE = 1 << 2
)
// RSeqCriticalSection describes a restartable sequences critical section. It
// is equivalent to struct rseq_cs, defined in include/uapi/linux/rseq.h.
//
// In userspace, this structure is always aligned to 32 bytes.
//
// +marshal
type RSeqCriticalSection struct {
// Version is the version of this structure. Version 0 is defined here.
Version uint32
// Flags are the critical section flags, defined above.
Flags uint32
// Start is the start address of the critical section.
Start uint64
// PostCommitOffset is the offset from Start of the first instruction
// outside of the critical section.
PostCommitOffset uint64
// Abort is the abort address. It must be outside the critical section,
// and the 4 bytes prior must match the abort signature.
Abort uint64
}
const (
// SizeOfRSeqCriticalSection is the size of RSeqCriticalSection.
SizeOfRSeqCriticalSection = 32
// SizeOfRSeqSignature is the size of the signature immediately
// preceding RSeqCriticalSection.Abort.
SizeOfRSeqSignature = 4
)
// Special values for RSeq.CPUID, defined in include/uapi/linux/rseq.h.
const (
// RSEQ_CPU_ID_UNINITIALIZED indicates that this thread has not
// performed rseq initialization.
RSEQ_CPU_ID_UNINITIALIZED = ^uint32(0) // -1
// RSEQ_CPU_ID_REGISTRATION_FAILED indicates that rseq initialization
// failed.
RSEQ_CPU_ID_REGISTRATION_FAILED = ^uint32(1) // -2
)
// RSeq is the thread-local restartable sequences config/status. It
// is equivalent to struct rseq, defined in include/uapi/linux/rseq.h.
//
// In userspace, this structure is always aligned to 32 bytes.
type RSeq struct {
// CPUIDStart contains the current CPU ID if rseq is initialized.
//
// This field should only be read by the thread which registered this
// structure, and must be read atomically.
CPUIDStart uint32
// CPUID contains the current CPU ID or one of the CPU ID special
// values defined above.
//
// This field should only be read by the thread which registered this
// structure, and must be read atomically.
CPUID uint32
// RSeqCriticalSection is a pointer to the current RSeqCriticalSection
// block, or NULL. It is reset to NULL by the kernel on restart or
// non-restarting preempt/signal.
//
// This field should only be written by the thread which registered
// this structure, and must be written atomically.
RSeqCriticalSection uint64
// Flags are the critical section flags that apply to all critical
// sections on this thread, defined above.
Flags uint32
}
const (
// SizeOfRSeq is the size of RSeq.
//
// Note that RSeq is naively 24 bytes. However, it has 32-byte
// alignment, which in C increases sizeof to 32. That is the size that
// the Linux kernel uses.
SizeOfRSeq = 32
// AlignOfRSeq is the standard alignment of RSeq.
AlignOfRSeq = 32
// OffsetOfRSeqCriticalSection is the offset of RSeqCriticalSection in RSeq.
OffsetOfRSeqCriticalSection = 8
)
+3 -3
View File
@@ -125,9 +125,9 @@ type Context interface {
// SetTLS sets the current TLS pointer. Returns false if value is invalid.
SetTLS(value uintptr) bool
// SetRSEQInterruptedIP sets the register that contains the old IP when a
// restartable sequence is interrupted.
SetRSEQInterruptedIP(value uintptr)
// SetOldRSeqInterruptedIP sets the register that contains the old IP
// when an "old rseq" restartable sequence is interrupted.
SetOldRSeqInterruptedIP(value uintptr)
// StateData returns a pointer to underlying architecture state.
StateData() *State
+2 -2
View File
@@ -174,8 +174,8 @@ func (c *context64) SetTLS(value uintptr) bool {
return true
}
// SetRSEQInterruptedIP implements Context.SetRSEQInterruptedIP.
func (c *context64) SetRSEQInterruptedIP(value uintptr) {
// SetOldRSeqInterruptedIP implements Context.SetOldRSeqInterruptedIP.
func (c *context64) SetOldRSeqInterruptedIP(value uintptr) {
c.Regs.R10 = uint64(value)
}
+339 -54
View File
@@ -15,17 +15,29 @@
package kernel
import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/hostcpu"
"gvisor.dev/gvisor/pkg/sentry/usermem"
"gvisor.dev/gvisor/pkg/syserror"
)
// Restartable sequences, as described in https://lwn.net/Articles/650333/.
// Restartable sequences.
//
// We support two different APIs for restartable sequences.
//
// 1. The upstream interface added in v4.18.
// 2. The interface described in https://lwn.net/Articles/650333/.
//
// Throughout this file and other parts of the kernel, the latter is referred
// to as "old rseq". This interface was never merged upstream, but is supported
// for a limited set of applications that use it regardless.
// RSEQCriticalRegion describes a restartable sequence critical region.
// OldRSeqCriticalRegion describes an old rseq critical region.
//
// +stateify savable
type RSEQCriticalRegion struct {
type OldRSeqCriticalRegion struct {
// When a task in this thread group has its CPU preempted (as defined by
// platform.ErrContextCPUPreempted) or has a signal delivered to an
// application handler while its instruction pointer is in CriticalSection,
@@ -35,86 +47,359 @@ type RSEQCriticalRegion struct {
Restart usermem.Addr
}
// RSEQAvailable returns true if t supports restartable sequences.
func (t *Task) RSEQAvailable() bool {
// RSeqAvailable returns true if t supports (old and new) restartable sequences.
func (t *Task) RSeqAvailable() bool {
return t.k.useHostCores && t.k.Platform.DetectsCPUPreemption()
}
// RSEQCriticalRegion returns a copy of t's thread group's current restartable
// sequence.
func (t *Task) RSEQCriticalRegion() RSEQCriticalRegion {
return *t.tg.rscr.Load().(*RSEQCriticalRegion)
}
// SetRSEQCriticalRegion replaces t's thread group's restartable sequence.
// SetRSeq registers addr as this thread's rseq structure.
//
// Preconditions: t.RSEQAvailable() == true.
func (t *Task) SetRSEQCriticalRegion(rscr RSEQCriticalRegion) error {
// These checks are somewhat more lenient than in Linux, which (bizarrely)
// requires rscr.CriticalSection to be non-empty and rscr.Restart to be
// outside of rscr.CriticalSection, even if rscr.CriticalSection.Start == 0
// (which disables the critical region).
if rscr.CriticalSection.Start == 0 {
rscr.CriticalSection.End = 0
rscr.Restart = 0
t.tg.rscr.Store(&rscr)
return nil
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) SetRSeq(addr usermem.Addr, length, signature uint32) error {
if t.rseqAddr != 0 {
if t.rseqAddr != addr {
return syserror.EINVAL
}
if t.rseqSignature != signature {
return syserror.EINVAL
}
return syserror.EBUSY
}
if rscr.CriticalSection.Start >= rscr.CriticalSection.End {
// rseq must be aligned and correctly sized.
if addr&(linux.AlignOfRSeq-1) != 0 {
return syserror.EINVAL
}
if rscr.CriticalSection.Contains(rscr.Restart) {
if length != linux.SizeOfRSeq {
return syserror.EINVAL
}
// TODO(jamieliu): check that rscr.CriticalSection and rscr.Restart are in
// the application address range, for consistency with Linux
t.tg.rscr.Store(&rscr)
if _, ok := t.MemoryManager().CheckIORange(addr, linux.SizeOfRSeq); !ok {
return syserror.EFAULT
}
t.rseqAddr = addr
t.rseqSignature = signature
// Initialize the CPUID.
//
// Linux implicitly does this on return from userspace, where failure
// would cause SIGSEGV.
if err := t.rseqUpdateCPU(); err != nil {
t.rseqAddr = 0
t.rseqSignature = 0
t.Debugf("Failed to copy CPU to %#x for rseq: %v", t.rseqAddr, err)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return syserror.EFAULT
}
return nil
}
// RSEQCPUAddr returns the address that RSEQ will keep updated with t's CPU
// number.
// ClearRSeq unregisters addr as this thread's rseq structure.
//
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) RSEQCPUAddr() usermem.Addr {
return t.rseqCPUAddr
func (t *Task) ClearRSeq(addr usermem.Addr, length, signature uint32) error {
if t.rseqAddr == 0 {
return syserror.EINVAL
}
if t.rseqAddr != addr {
return syserror.EINVAL
}
if length != linux.SizeOfRSeq {
return syserror.EINVAL
}
if t.rseqSignature != signature {
return syserror.EPERM
}
if err := t.rseqClearCPU(); err != nil {
return err
}
t.rseqAddr = 0
t.rseqSignature = 0
if t.oldRSeqCPUAddr == 0 {
// rseqCPU no longer needed.
t.rseqCPU = -1
}
return nil
}
// SetRSEQCPUAddr replaces the address that RSEQ will keep updated with t's CPU
// number.
// OldRSeqCriticalRegion returns a copy of t's thread group's current
// old restartable sequence.
func (t *Task) OldRSeqCriticalRegion() OldRSeqCriticalRegion {
return *t.tg.oldRSeqCritical.Load().(*OldRSeqCriticalRegion)
}
// SetOldRSeqCriticalRegion replaces t's thread group's old restartable
// sequence.
//
// Preconditions: t.RSEQAvailable() == true. The caller must be running on the
// Preconditions: t.RSeqAvailable() == true.
func (t *Task) SetOldRSeqCriticalRegion(r OldRSeqCriticalRegion) error {
// These checks are somewhat more lenient than in Linux, which (bizarrely)
// requires r.CriticalSection to be non-empty and r.Restart to be
// outside of r.CriticalSection, even if r.CriticalSection.Start == 0
// (which disables the critical region).
if r.CriticalSection.Start == 0 {
r.CriticalSection.End = 0
r.Restart = 0
t.tg.oldRSeqCritical.Store(&r)
return nil
}
if r.CriticalSection.Start >= r.CriticalSection.End {
return syserror.EINVAL
}
if r.CriticalSection.Contains(r.Restart) {
return syserror.EINVAL
}
// TODO(jamieliu): check that r.CriticalSection and r.Restart are in
// the application address range, for consistency with Linux.
t.tg.oldRSeqCritical.Store(&r)
return nil
}
// OldRSeqCPUAddr returns the address that old rseq will keep updated with t's
// CPU number.
//
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) OldRSeqCPUAddr() usermem.Addr {
return t.oldRSeqCPUAddr
}
// SetOldRSeqCPUAddr replaces the address that old rseq will keep updated with
// t's CPU number.
//
// Preconditions: t.RSeqAvailable() == true. The caller must be running on the
// task goroutine. t's AddressSpace must be active.
func (t *Task) SetRSEQCPUAddr(addr usermem.Addr) error {
t.rseqCPUAddr = addr
if addr != 0 {
t.rseqCPU = int32(hostcpu.GetCPU())
if err := t.rseqCopyOutCPU(); err != nil {
t.rseqCPUAddr = 0
t.rseqCPU = -1
return syserror.EINVAL // yes, EINVAL, not err or EFAULT
}
} else {
t.rseqCPU = -1
func (t *Task) SetOldRSeqCPUAddr(addr usermem.Addr) error {
t.oldRSeqCPUAddr = addr
// Check that addr is writable.
//
// N.B. rseqUpdateCPU may fail on a bad t.rseqAddr as well. That's
// unfortunate, but unlikely in a correct program.
if err := t.rseqUpdateCPU(); err != nil {
t.oldRSeqCPUAddr = 0
return syserror.EINVAL // yes, EINVAL, not err or EFAULT
}
return nil
}
// Preconditions: The caller must be running on the task goroutine. t's
// AddressSpace must be active.
func (t *Task) rseqCopyOutCPU() error {
func (t *Task) rseqUpdateCPU() error {
if t.rseqAddr == 0 && t.oldRSeqCPUAddr == 0 {
t.rseqCPU = -1
return nil
}
t.rseqCPU = int32(hostcpu.GetCPU())
// Update both CPUs, even if one fails.
rerr := t.rseqCopyOutCPU()
oerr := t.oldRSeqCopyOutCPU()
if rerr != nil {
return rerr
}
return oerr
}
// Preconditions: The caller must be running on the task goroutine. t's
// AddressSpace must be active.
func (t *Task) oldRSeqCopyOutCPU() error {
if t.oldRSeqCPUAddr == 0 {
return nil
}
buf := t.CopyScratchBuffer(4)
usermem.ByteOrder.PutUint32(buf, uint32(t.rseqCPU))
_, err := t.CopyOutBytes(t.rseqCPUAddr, buf)
_, err := t.CopyOutBytes(t.oldRSeqCPUAddr, buf)
return err
}
// Preconditions: The caller must be running on the task goroutine. t's
// AddressSpace must be active.
func (t *Task) rseqCopyOutCPU() error {
if t.rseqAddr == 0 {
return nil
}
buf := t.CopyScratchBuffer(8)
// CPUIDStart and CPUID are the first two fields in linux.RSeq.
usermem.ByteOrder.PutUint32(buf, uint32(t.rseqCPU)) // CPUIDStart
usermem.ByteOrder.PutUint32(buf[4:], uint32(t.rseqCPU)) // CPUID
// N.B. This write is not atomic, but since this occurs on the task
// goroutine then as long as userspace uses a single-instruction read
// it can't see an invalid value.
_, err := t.CopyOutBytes(t.rseqAddr, buf)
return err
}
// Preconditions: The caller must be running on the task goroutine. t's
// AddressSpace must be active.
func (t *Task) rseqClearCPU() error {
buf := t.CopyScratchBuffer(8)
// CPUIDStart and CPUID are the first two fields in linux.RSeq.
usermem.ByteOrder.PutUint32(buf, 0) // CPUIDStart
usermem.ByteOrder.PutUint32(buf[4:], linux.RSEQ_CPU_ID_UNINITIALIZED) // CPUID
// N.B. This write is not atomic, but since this occurs on the task
// goroutine then as long as userspace uses a single-instruction read
// it can't see an invalid value.
_, err := t.CopyOutBytes(t.rseqAddr, buf)
return err
}
// rseqAddrInterrupt checks if IP is in a critical section, and aborts if so.
//
// This is a bit complex since both the RSeq and RSeqCriticalSection structs
// are stored in userspace. So we must:
//
// 1. Copy in the address of RSeqCriticalSection from RSeq.
// 2. Copy in RSeqCriticalSection itself.
// 3. Validate critical section struct version, address range, abort address.
// 4. Validate the abort signature (4 bytes preceding abort IP match expected
// signature).
// 5. Clear address of RSeqCriticalSection from RSeq.
// 6. Finally, conditionally abort.
//
// See kernel/rseq.c:rseq_ip_fixup for reference.
//
// Preconditions: The caller must be running on the task goroutine. t's
// AddressSpace must be active.
func (t *Task) rseqAddrInterrupt() {
if t.rseqAddr == 0 {
return
}
critAddrAddr, ok := t.rseqAddr.AddLength(linux.OffsetOfRSeqCriticalSection)
if !ok {
// SetRSeq should validate this.
panic(fmt.Sprintf("t.rseqAddr (%#x) not large enough", t.rseqAddr))
}
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
t.Debugf("Only 64-bit rseq supported.")
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
buf := t.CopyScratchBuffer(8)
if _, err := t.CopyInBytes(critAddrAddr, buf); err != nil {
t.Debugf("Failed to copy critical section address from %#x for rseq: %v", critAddrAddr, err)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
critAddr := usermem.Addr(usermem.ByteOrder.Uint64(buf))
if critAddr == 0 {
return
}
buf = t.CopyScratchBuffer(linux.SizeOfRSeqCriticalSection)
if _, err := t.CopyInBytes(critAddr, buf); err != nil {
t.Debugf("Failed to copy critical section from %#x for rseq: %v", critAddr, err)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
// Manually marshal RSeqCriticalSection as this is in the hot path when
// rseq is enabled. It must be as fast as possible.
//
// TODO(b/130243041): Replace with go_marshal.
cs := linux.RSeqCriticalSection{
Version: usermem.ByteOrder.Uint32(buf[0:4]),
Flags: usermem.ByteOrder.Uint32(buf[4:8]),
Start: usermem.ByteOrder.Uint64(buf[8:16]),
PostCommitOffset: usermem.ByteOrder.Uint64(buf[16:24]),
Abort: usermem.ByteOrder.Uint64(buf[24:32]),
}
if cs.Version != 0 {
t.Debugf("Unknown version in %+v", cs)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
start := usermem.Addr(cs.Start)
critRange, ok := start.ToRange(cs.PostCommitOffset)
if !ok {
t.Debugf("Invalid start and offset in %+v", cs)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
abort := usermem.Addr(cs.Abort)
if critRange.Contains(abort) {
t.Debugf("Abort in critical section in %+v", cs)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
// Verify signature.
sigAddr := abort - linux.SizeOfRSeqSignature
buf = t.CopyScratchBuffer(linux.SizeOfRSeqSignature)
if _, err := t.CopyInBytes(sigAddr, buf); err != nil {
t.Debugf("Failed to copy critical section signature from %#x for rseq: %v", sigAddr, err)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
sig := usermem.ByteOrder.Uint32(buf)
if sig != t.rseqSignature {
t.Debugf("Mismatched rseq signature %d != %d", sig, t.rseqSignature)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
// Clear the critical section address.
//
// NOTE(b/143949567): We don't support any rseq flags, so we always
// restart if we are in the critical section, and thus *always* clear
// critAddrAddr.
if _, err := t.MemoryManager().ZeroOut(t, critAddrAddr, int64(t.Arch().Width()), usermem.IOOpts{
AddressSpaceActive: true,
}); err != nil {
t.Debugf("Failed to clear critical section address from %#x for rseq: %v", critAddrAddr, err)
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
return
}
// Finally we can actually decide whether or not to restart.
if !critRange.Contains(usermem.Addr(t.Arch().IP())) {
return
}
t.Arch().SetIP(uintptr(cs.Abort))
}
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) oldRSeqInterrupt() {
r := t.tg.oldRSeqCritical.Load().(*OldRSeqCriticalRegion)
if ip := t.Arch().IP(); r.CriticalSection.Contains(usermem.Addr(ip)) {
t.Debugf("Interrupted rseq critical section at %#x; restarting at %#x", ip, r.Restart)
t.Arch().SetIP(uintptr(r.Restart))
t.Arch().SetOldRSeqInterruptedIP(ip)
}
}
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) rseqInterrupt() {
rscr := t.tg.rscr.Load().(*RSEQCriticalRegion)
if ip := t.Arch().IP(); rscr.CriticalSection.Contains(usermem.Addr(ip)) {
t.Debugf("Interrupted RSEQ critical section at %#x; restarting at %#x", ip, rscr.Restart)
t.Arch().SetIP(uintptr(rscr.Restart))
t.Arch().SetRSEQInterruptedIP(ip)
}
t.rseqAddrInterrupt()
t.oldRSeqInterrupt()
}
+34 -9
View File
@@ -489,18 +489,43 @@ type Task struct {
// netns is protected by mu. netns is owned by the task goroutine.
netns bool
// If rseqPreempted is true, before the next call to p.Switch(), interrupt
// RSEQ critical regions as defined by tg.rseq and write the task
// goroutine's CPU number to rseqCPUAddr. rseqCPU is the last CPU number
// written to rseqCPUAddr.
// If rseqPreempted is true, before the next call to p.Switch(),
// interrupt rseq critical regions as defined by rseqAddr and
// tg.oldRSeqCritical and write the task goroutine's CPU number to
// rseqAddr/oldRSeqCPUAddr.
//
// If rseqCPUAddr is 0, rseqCPU is -1.
// We support two ABIs for restartable sequences:
//
// rseqCPUAddr, rseqCPU, and rseqPreempted are exclusive to the task
// goroutine.
// 1. The upstream interface added in v4.18,
// 2. An "old" interface never merged upstream. In the implementation,
// this is referred to as "old rseq".
//
// rseqPreempted is exclusive to the task goroutine.
rseqPreempted bool `state:"nosave"`
rseqCPUAddr usermem.Addr
rseqCPU int32
// rseqCPU is the last CPU number written to rseqAddr/oldRSeqCPUAddr.
//
// If rseq is unused, rseqCPU is -1 for convenient use in
// platform.Context.Switch.
//
// rseqCPU is exclusive to the task goroutine.
rseqCPU int32
// oldRSeqCPUAddr is a pointer to the userspace old rseq CPU variable.
//
// oldRSeqCPUAddr is exclusive to the task goroutine.
oldRSeqCPUAddr usermem.Addr
// rseqAddr is a pointer to the userspace linux.RSeq structure.
//
// rseqAddr is exclusive to the task goroutine.
rseqAddr usermem.Addr
// rseqSignature is the signature that the rseq abort IP must be signed
// with.
//
// rseqSignature is exclusive to the task goroutine.
rseqSignature uint32
// copyScratchBuffer is a buffer available to CopyIn/CopyOut
// implementations that require an intermediate buffer to copy data
+7
View File
@@ -236,7 +236,10 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
} else if opts.NewPIDNamespace {
pidns = pidns.NewChild(userns)
}
tg := t.tg
rseqAddr := usermem.Addr(0)
rseqSignature := uint32(0)
if opts.NewThreadGroup {
tg.mounts.IncRef()
sh := t.tg.signalHandlers
@@ -244,6 +247,8 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
sh = sh.Fork()
}
tg = t.k.NewThreadGroup(tg.mounts, pidns, sh, opts.TerminationSignal, tg.limits.GetCopy())
rseqAddr = t.rseqAddr
rseqSignature = t.rseqSignature
}
cfg := &TaskConfig{
@@ -260,6 +265,8 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
UTSNamespace: utsns,
IPCNamespace: ipcns,
AbstractSocketNamespace: t.abstractSockets,
RSeqAddr: rseqAddr,
RSeqSignature: rseqSignature,
ContainerID: t.ContainerID(),
}
if opts.NewThreadGroup {
+4 -2
View File
@@ -190,9 +190,11 @@ func (r *runSyscallAfterExecStop) execute(t *Task) taskRunState {
t.updateRSSLocked()
// Restartable sequence state is discarded.
t.rseqPreempted = false
t.rseqCPUAddr = 0
t.rseqCPU = -1
t.tg.rscr.Store(&RSEQCriticalRegion{})
t.rseqAddr = 0
t.rseqSignature = 0
t.oldRSeqCPUAddr = 0
t.tg.oldRSeqCritical.Store(&OldRSeqCriticalRegion{})
t.tg.pidns.owner.mu.Unlock()
// Remove FDs with the CloseOnExec flag set.
+13 -3
View File
@@ -169,12 +169,22 @@ func (*runApp) execute(t *Task) taskRunState {
// Apply restartable sequences.
if t.rseqPreempted {
t.rseqPreempted = false
if t.rseqCPUAddr != 0 {
if t.rseqAddr != 0 || t.oldRSeqCPUAddr != 0 {
// Linux writes the CPU on every preemption. We only do
// so if it changed. Thus we may delay delivery of
// SIGSEGV if rseqAddr/oldRSeqCPUAddr is invalid.
cpu := int32(hostcpu.GetCPU())
if t.rseqCPU != cpu {
t.rseqCPU = cpu
if err := t.rseqCopyOutCPU(); err != nil {
t.Warningf("Failed to copy CPU to %#x for RSEQ: %v", t.rseqCPUAddr, err)
t.Debugf("Failed to copy CPU to %#x for rseq: %v", t.rseqAddr, err)
t.forceSignal(linux.SIGSEGV, false)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
// Re-enter the task run loop for signal delivery.
return (*runApp)(nil)
}
if err := t.oldRSeqCopyOutCPU(); err != nil {
t.Debugf("Failed to copy CPU to %#x for old rseq: %v", t.oldRSeqCPUAddr, err)
t.forceSignal(linux.SIGSEGV, false)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
// Re-enter the task run loop for signal delivery.
@@ -320,7 +330,7 @@ func (*runApp) execute(t *Task) taskRunState {
return (*runApp)(nil)
case platform.ErrContextCPUPreempted:
// Ensure that RSEQ critical sections are interrupted and per-thread
// Ensure that rseq critical sections are interrupted and per-thread
// CPU values are updated before the next platform.Context.Switch().
t.rseqPreempted = true
return (*runApp)(nil)
+10
View File
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel/futex"
"gvisor.dev/gvisor/pkg/sentry/kernel/sched"
"gvisor.dev/gvisor/pkg/sentry/usage"
"gvisor.dev/gvisor/pkg/sentry/usermem"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -79,6 +80,13 @@ type TaskConfig struct {
// AbstractSocketNamespace is the AbstractSocketNamespace of the new task.
AbstractSocketNamespace *AbstractSocketNamespace
// RSeqAddr is a pointer to the the userspace linux.RSeq structure.
RSeqAddr usermem.Addr
// RSeqSignature is the signature that the rseq abort IP must be signed
// with.
RSeqSignature uint32
// ContainerID is the container the new task belongs to.
ContainerID string
}
@@ -126,6 +134,8 @@ func (ts *TaskSet) newTask(cfg *TaskConfig) (*Task, error) {
ipcns: cfg.IPCNamespace,
abstractSockets: cfg.AbstractSocketNamespace,
rseqCPU: -1,
rseqAddr: cfg.RSeqAddr,
rseqSignature: cfg.RSeqSignature,
futexWaiter: futex.NewWaiter(),
containerID: cfg.ContainerID,
}
+9 -9
View File
@@ -238,8 +238,8 @@ type ThreadGroup struct {
// execed is protected by the TaskSet mutex.
execed bool
// rscr is the thread group's RSEQ critical region.
rscr atomic.Value `state:".(*RSEQCriticalRegion)"`
// oldRSeqCritical is the thread group's old rseq critical region.
oldRSeqCritical atomic.Value `state:".(*OldRSeqCriticalRegion)"`
// mounts is the thread group's mount namespace. This does not really
// correspond to a "mount namespace" in Linux, but is more like a
@@ -273,18 +273,18 @@ func (k *Kernel) NewThreadGroup(mntns *fs.MountNamespace, pidns *PIDNamespace, s
}
tg.itimerRealTimer = ktime.NewTimer(k.monotonicClock, &itimerRealListener{tg: tg})
tg.timers = make(map[linux.TimerID]*IntervalTimer)
tg.rscr.Store(&RSEQCriticalRegion{})
tg.oldRSeqCritical.Store(&OldRSeqCriticalRegion{})
return tg
}
// saveRscr is invoked by stateify.
func (tg *ThreadGroup) saveRscr() *RSEQCriticalRegion {
return tg.rscr.Load().(*RSEQCriticalRegion)
// saveOldRSeqCritical is invoked by stateify.
func (tg *ThreadGroup) saveOldRSeqCritical() *OldRSeqCriticalRegion {
return tg.oldRSeqCritical.Load().(*OldRSeqCriticalRegion)
}
// loadRscr is invoked by stateify.
func (tg *ThreadGroup) loadRscr(rscr *RSEQCriticalRegion) {
tg.rscr.Store(rscr)
// loadOldRSeqCritical is invoked by stateify.
func (tg *ThreadGroup) loadOldRSeqCritical(r *OldRSeqCriticalRegion) {
tg.oldRSeqCritical.Store(r)
}
// SignalHandlers returns the signal handlers used by tg.
+1
View File
@@ -30,6 +30,7 @@ go_library(
"sys_random.go",
"sys_read.go",
"sys_rlimit.go",
"sys_rseq.go",
"sys_rusage.go",
"sys_sched.go",
"sys_seccomp.go",
+1 -1
View File
@@ -377,7 +377,7 @@ var AMD64 = &kernel.SyscallTable{
331: syscalls.ErrorWithEvent("pkey_free", syserror.ENOSYS, "", nil),
332: syscalls.Supported("statx", Statx),
333: syscalls.ErrorWithEvent("io_pgetevents", syserror.ENOSYS, "", nil),
334: syscalls.ErrorWithEvent("rseq", syserror.ENOSYS, "", nil),
334: syscalls.PartiallySupported("rseq", RSeq, "Not supported on all platforms.", nil),
// Linux skips ahead to syscall 424 to sync numbers between arches.
424: syscalls.ErrorWithEvent("pidfd_send_signal", syserror.ENOSYS, "", nil),
+1 -1
View File
@@ -307,7 +307,7 @@ var ARM64 = &kernel.SyscallTable{
290: syscalls.ErrorWithEvent("pkey_free", syserror.ENOSYS, "", nil),
291: syscalls.Supported("statx", Statx),
292: syscalls.ErrorWithEvent("io_pgetevents", syserror.ENOSYS, "", nil),
293: syscalls.ErrorWithEvent("rseq", syserror.ENOSYS, "", nil),
293: syscalls.PartiallySupported("rseq", RSeq, "Not supported on all platforms.", nil),
// Linux skips ahead to syscall 424 to sync numbers between arches.
424: syscalls.ErrorWithEvent("pidfd_send_signal", syserror.ENOSYS, "", nil),
+48
View File
@@ -0,0 +1,48 @@
// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
)
// RSeq implements syscall rseq(2).
func RSeq(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
addr := args[0].Pointer()
length := args[1].Uint()
flags := args[2].Int()
signature := args[3].Uint()
if !t.RSeqAvailable() {
// Event for applications that want rseq on a configuration
// that doesn't support them.
t.Kernel().EmitUnimplementedEvent(t)
return 0, nil, syserror.ENOSYS
}
switch flags {
case 0:
// Register.
return 0, nil, t.SetRSeq(addr, length, signature)
case linux.RSEQ_FLAG_UNREGISTER:
return 0, nil, t.ClearRSeq(addr, length, signature)
default:
// Unknown flag.
return 0, nil, syserror.EINVAL
}
}