mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Replace remaining uses of reflection-based marshalling.
- Rewrite arch.Stack.{Push,Pop}. For the most part, stack now
implements marshal.CopyContext and can be used as the target of
marshal operations. Stack.Push had some extra logic for
automatically null-terminating slices. This was only used for two
specific types of slices, and is now handled explicitly.
- Delete usermem.CopyObject{In,Out}.
- Replace most remaining uses of the encoding/binary package with
go-marshal. Most of these were using the binary package to compute
the size of a struct, which go-marshal can directly replace. ~3 uses
of the binary package remain. These aren't reasonably replaceable by
go-marshal: for example one use is to construct the syscall
trampoline for systrap.
- Fill out remaining convenience wrappers in the primitive package.
PiperOrigin-RevId: 334502375
This commit is contained in:
committed by
gVisor bot
parent
fee2c07728
commit
387501219e
+1
-3
@@ -11,7 +11,5 @@ go_library(
|
||||
visibility = [
|
||||
"//:sandbox",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/usermem",
|
||||
],
|
||||
deps = ["//pkg/usermem"],
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ go_library(
|
||||
"//:sandbox",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/context",
|
||||
"//pkg/marshal",
|
||||
"//pkg/usermem",
|
||||
],
|
||||
|
||||
@@ -19,6 +19,7 @@ package primitive
|
||||
import (
|
||||
"io"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/marshal"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
@@ -126,6 +127,46 @@ var _ marshal.Marshallable = (*ByteSlice)(nil)
|
||||
// Below, we define some convenience functions for marshalling primitive types
|
||||
// using the newtypes above, without requiring superfluous casts.
|
||||
|
||||
// 8-bit integers
|
||||
|
||||
// CopyInt8In is a convenient wrapper for copying in an int8 from the task's
|
||||
// memory.
|
||||
func CopyInt8In(cc marshal.CopyContext, addr usermem.Addr, dst *int8) (int, error) {
|
||||
var buf Int8
|
||||
n, err := buf.CopyIn(cc, addr)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
*dst = int8(buf)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// CopyInt8Out is a convenient wrapper for copying out an int8 to the task's
|
||||
// memory.
|
||||
func CopyInt8Out(cc marshal.CopyContext, addr usermem.Addr, src int8) (int, error) {
|
||||
srcP := Int8(src)
|
||||
return srcP.CopyOut(cc, addr)
|
||||
}
|
||||
|
||||
// CopyUint8In is a convenient wrapper for copying in a uint8 from the task's
|
||||
// memory.
|
||||
func CopyUint8In(cc marshal.CopyContext, addr usermem.Addr, dst *uint8) (int, error) {
|
||||
var buf Uint8
|
||||
n, err := buf.CopyIn(cc, addr)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
*dst = uint8(buf)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// CopyUint8Out is a convenient wrapper for copying out a uint8 to the task's
|
||||
// memory.
|
||||
func CopyUint8Out(cc marshal.CopyContext, addr usermem.Addr, src uint8) (int, error) {
|
||||
srcP := Uint8(src)
|
||||
return srcP.CopyOut(cc, addr)
|
||||
}
|
||||
|
||||
// 16-bit integers
|
||||
|
||||
// CopyInt16In is a convenient wrapper for copying in an int16 from the task's
|
||||
@@ -245,3 +286,64 @@ func CopyUint64Out(cc marshal.CopyContext, addr usermem.Addr, src uint64) (int,
|
||||
srcP := Uint64(src)
|
||||
return srcP.CopyOut(cc, addr)
|
||||
}
|
||||
|
||||
// CopyByteSliceIn is a convenient wrapper for copying in a []byte from the
|
||||
// task's memory.
|
||||
func CopyByteSliceIn(cc marshal.CopyContext, addr usermem.Addr, dst *[]byte) (int, error) {
|
||||
var buf ByteSlice
|
||||
n, err := buf.CopyIn(cc, addr)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
*dst = []byte(buf)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// CopyByteSliceOut is a convenient wrapper for copying out a []byte to the
|
||||
// task's memory.
|
||||
func CopyByteSliceOut(cc marshal.CopyContext, addr usermem.Addr, src []byte) (int, error) {
|
||||
srcP := ByteSlice(src)
|
||||
return srcP.CopyOut(cc, addr)
|
||||
}
|
||||
|
||||
// CopyStringIn is a convenient wrapper for copying in a string from the
|
||||
// task's memory.
|
||||
func CopyStringIn(cc marshal.CopyContext, addr usermem.Addr, dst *string) (int, error) {
|
||||
var buf ByteSlice
|
||||
n, err := buf.CopyIn(cc, addr)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
*dst = string(buf)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// CopyStringOut is a convenient wrapper for copying out a string to the task's
|
||||
// memory.
|
||||
func CopyStringOut(cc marshal.CopyContext, addr usermem.Addr, src string) (int, error) {
|
||||
srcP := ByteSlice(src)
|
||||
return srcP.CopyOut(cc, addr)
|
||||
}
|
||||
|
||||
// IOCopyContext wraps an object implementing usermem.IO to implement
|
||||
// marshal.CopyContext.
|
||||
type IOCopyContext struct {
|
||||
Ctx context.Context
|
||||
IO usermem.IO
|
||||
Opts usermem.IOOpts
|
||||
}
|
||||
|
||||
// CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer.
|
||||
func (i *IOCopyContext) CopyScratchBuffer(size int) []byte {
|
||||
return make([]byte, size)
|
||||
}
|
||||
|
||||
// CopyOutBytes implements marshal.CopyContext.CopyOutBytes.
|
||||
func (i *IOCopyContext) CopyOutBytes(addr usermem.Addr, b []byte) (int, error) {
|
||||
return i.IO.CopyOut(i.Ctx, addr, b, i.Opts)
|
||||
}
|
||||
|
||||
// CopyInBytes implements marshal.CopyContext.CopyInBytes.
|
||||
func (i *IOCopyContext) CopyInBytes(addr usermem.Addr, b []byte) (int, error) {
|
||||
return i.IO.CopyIn(i.Ctx, addr, b, i.Opts)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ go_library(
|
||||
"signal_info.go",
|
||||
"signal_stack.go",
|
||||
"stack.go",
|
||||
"stack_unsafe.go",
|
||||
"syscalls_amd64.go",
|
||||
"syscalls_arm64.go",
|
||||
],
|
||||
|
||||
@@ -17,17 +17,19 @@
|
||||
package arch
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// SignalContext64 is equivalent to struct sigcontext, the type passed as the
|
||||
// second argument to signal handlers set by signal(2).
|
||||
//
|
||||
// +marshal
|
||||
type SignalContext64 struct {
|
||||
R8 uint64
|
||||
R9 uint64
|
||||
@@ -68,6 +70,8 @@ const (
|
||||
)
|
||||
|
||||
// UContext64 is equivalent to ucontext_t on 64-bit x86.
|
||||
//
|
||||
// +marshal
|
||||
type UContext64 struct {
|
||||
Flags uint64
|
||||
Link uint64
|
||||
@@ -172,12 +176,7 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
|
||||
|
||||
// "... the value (%rsp+8) is always a multiple of 16 (...) when
|
||||
// control is transferred to the function entry point." - AMD64 ABI
|
||||
ucSize := binary.Size(uc)
|
||||
if ucSize < 0 {
|
||||
// This can only happen if we've screwed up the definition of
|
||||
// UContext64.
|
||||
panic("can't get size of UContext64")
|
||||
}
|
||||
ucSize := uc.SizeBytes()
|
||||
// st.Arch.Width() is for the restorer address. sizeof(siginfo) == 128.
|
||||
frameSize := int(st.Arch.Width()) + ucSize + 128
|
||||
frameBottom := (sp-usermem.Addr(frameSize)) & ^usermem.Addr(15) - 8
|
||||
@@ -195,18 +194,18 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
|
||||
info.FixSignalCodeForUser()
|
||||
|
||||
// Set up the stack frame.
|
||||
infoAddr, err := st.Push(info)
|
||||
if err != nil {
|
||||
if _, err := info.CopyOut(st, StackBottomMagic); err != nil {
|
||||
return err
|
||||
}
|
||||
ucAddr, err := st.Push(uc)
|
||||
if err != nil {
|
||||
infoAddr := st.Bottom
|
||||
if _, err := uc.CopyOut(st, StackBottomMagic); err != nil {
|
||||
return err
|
||||
}
|
||||
ucAddr := st.Bottom
|
||||
if act.HasRestorer() {
|
||||
// Push the restorer return address.
|
||||
// Note that this doesn't need to be popped.
|
||||
if _, err := st.Push(usermem.Addr(act.Restorer)); err != nil {
|
||||
if _, err := primitive.CopyUint64Out(st, StackBottomMagic, act.Restorer); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@@ -240,11 +239,11 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
|
||||
func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, SignalStack, error) {
|
||||
// Copy out the stack frame.
|
||||
var uc UContext64
|
||||
if _, err := st.Pop(&uc); err != nil {
|
||||
if _, err := uc.CopyIn(st, StackBottomMagic); err != nil {
|
||||
return 0, SignalStack{}, err
|
||||
}
|
||||
var info SignalInfo
|
||||
if _, err := st.Pop(&info); err != nil {
|
||||
if _, err := info.CopyIn(st, StackBottomMagic); err != nil {
|
||||
return 0, SignalStack{}, err
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
@@ -25,6 +26,8 @@ import (
|
||||
|
||||
// SignalContext64 is equivalent to struct sigcontext, the type passed as the
|
||||
// second argument to signal handlers set by signal(2).
|
||||
//
|
||||
// +marshal
|
||||
type SignalContext64 struct {
|
||||
FaultAddr uint64
|
||||
Regs [31]uint64
|
||||
@@ -36,6 +39,7 @@ type SignalContext64 struct {
|
||||
Reserved [3568]uint8
|
||||
}
|
||||
|
||||
// +marshal
|
||||
type aarch64Ctx struct {
|
||||
Magic uint32
|
||||
Size uint32
|
||||
@@ -43,6 +47,8 @@ type aarch64Ctx struct {
|
||||
|
||||
// FpsimdContext is equivalent to struct fpsimd_context on arm64
|
||||
// (arch/arm64/include/uapi/asm/sigcontext.h).
|
||||
//
|
||||
// +marshal
|
||||
type FpsimdContext struct {
|
||||
Head aarch64Ctx
|
||||
Fpsr uint32
|
||||
@@ -51,13 +57,15 @@ type FpsimdContext struct {
|
||||
}
|
||||
|
||||
// UContext64 is equivalent to ucontext on arm64(arch/arm64/include/uapi/asm/ucontext.h).
|
||||
//
|
||||
// +marshal
|
||||
type UContext64 struct {
|
||||
Flags uint64
|
||||
Link uint64
|
||||
Stack SignalStack
|
||||
Sigset linux.SignalSet
|
||||
// glibc uses a 1024-bit sigset_t
|
||||
_pad [(1024 - 64) / 8]byte
|
||||
_pad [120]byte // (1024 - 64) / 8 = 120
|
||||
// sigcontext must be aligned to 16-byte
|
||||
_pad2 [8]byte
|
||||
// last for future expansion
|
||||
@@ -94,11 +102,7 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
|
||||
},
|
||||
Sigset: sigset,
|
||||
}
|
||||
|
||||
ucSize := binary.Size(uc)
|
||||
if ucSize < 0 {
|
||||
panic("can't get size of UContext64")
|
||||
}
|
||||
ucSize := uc.SizeBytes()
|
||||
|
||||
// frameSize = ucSize + sizeof(siginfo).
|
||||
// sizeof(siginfo) == 128.
|
||||
@@ -119,14 +123,14 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
|
||||
info.FixSignalCodeForUser()
|
||||
|
||||
// Set up the stack frame.
|
||||
infoAddr, err := st.Push(info)
|
||||
if err != nil {
|
||||
if _, err := info.CopyOut(st, StackBottomMagic); err != nil {
|
||||
return err
|
||||
}
|
||||
ucAddr, err := st.Push(uc)
|
||||
if err != nil {
|
||||
infoAddr := st.Bottom
|
||||
if _, err := uc.CopyOut(st, StackBottomMagic); err != nil {
|
||||
return err
|
||||
}
|
||||
ucAddr := st.Bottom
|
||||
|
||||
// Set up registers.
|
||||
c.Regs.Sp = uint64(st.Bottom)
|
||||
@@ -147,11 +151,11 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
|
||||
func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, SignalStack, error) {
|
||||
// Copy out the stack frame.
|
||||
var uc UContext64
|
||||
if _, err := st.Pop(&uc); err != nil {
|
||||
if _, err := uc.CopyIn(st, StackBottomMagic); err != nil {
|
||||
return 0, SignalStack{}, err
|
||||
}
|
||||
var info SignalInfo
|
||||
if _, err := st.Pop(&info); err != nil {
|
||||
if _, err := info.CopyIn(st, StackBottomMagic); err != nil {
|
||||
return 0, SignalStack{}, err
|
||||
}
|
||||
|
||||
|
||||
+74
-103
@@ -15,14 +15,16 @@
|
||||
package arch
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// Stack is a simple wrapper around a usermem.IO and an address.
|
||||
// Stack is a simple wrapper around a usermem.IO and an address. Stack
|
||||
// implements marshal.CopyContext, and marshallable values can be pushed or
|
||||
// popped from the stack through the marshal.Marshallable interface.
|
||||
//
|
||||
// Stack is not thread-safe.
|
||||
type Stack struct {
|
||||
// Our arch info.
|
||||
// We use this for automatic Native conversion of usermem.Addrs during
|
||||
@@ -34,105 +36,60 @@ type Stack struct {
|
||||
|
||||
// Our current stack bottom.
|
||||
Bottom usermem.Addr
|
||||
|
||||
// Scratch buffer used for marshalling to avoid having to repeatedly
|
||||
// allocate scratch memory.
|
||||
scratchBuf []byte
|
||||
}
|
||||
|
||||
// Push pushes the given values on to the stack.
|
||||
//
|
||||
// (This method supports Addrs and treats them as native types.)
|
||||
func (s *Stack) Push(vals ...interface{}) (usermem.Addr, error) {
|
||||
for _, v := range vals {
|
||||
// scratchBufLen is the default length of Stack.scratchBuf. The
|
||||
// largest structs the stack regularly serializes are arch.SignalInfo
|
||||
// and arch.UContext64. We'll set the default size as the larger of
|
||||
// the two, arch.UContext64.
|
||||
var scratchBufLen = (*UContext64)(nil).SizeBytes()
|
||||
|
||||
// We convert some types to well-known serializable quanities.
|
||||
var norm interface{}
|
||||
// CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer.
|
||||
func (s *Stack) CopyScratchBuffer(size int) []byte {
|
||||
if len(s.scratchBuf) < size {
|
||||
s.scratchBuf = make([]byte, size)
|
||||
}
|
||||
return s.scratchBuf[:size]
|
||||
}
|
||||
|
||||
// For array types, we will automatically add an appropriate
|
||||
// terminal value. This is done simply to make the interface
|
||||
// easier to use.
|
||||
var term interface{}
|
||||
|
||||
switch v.(type) {
|
||||
case string:
|
||||
norm = []byte(v.(string))
|
||||
term = byte(0)
|
||||
case []int8, []uint8:
|
||||
norm = v
|
||||
term = byte(0)
|
||||
case []int16, []uint16:
|
||||
norm = v
|
||||
term = uint16(0)
|
||||
case []int32, []uint32:
|
||||
norm = v
|
||||
term = uint32(0)
|
||||
case []int64, []uint64:
|
||||
norm = v
|
||||
term = uint64(0)
|
||||
case []usermem.Addr:
|
||||
// Special case: simply push recursively.
|
||||
_, err := s.Push(s.Arch.Native(uintptr(0)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
varr := v.([]usermem.Addr)
|
||||
for i := len(varr) - 1; i >= 0; i-- {
|
||||
_, err := s.Push(varr[i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
continue
|
||||
case usermem.Addr:
|
||||
norm = s.Arch.Native(uintptr(v.(usermem.Addr)))
|
||||
default:
|
||||
norm = v
|
||||
}
|
||||
|
||||
if term != nil {
|
||||
_, err := s.Push(term)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
c := binary.Size(norm)
|
||||
if c < 0 {
|
||||
return 0, fmt.Errorf("bad binary.Size for %T", v)
|
||||
}
|
||||
n, err := usermem.CopyObjectOut(context.Background(), s.IO, s.Bottom-usermem.Addr(c), norm, usermem.IOOpts{})
|
||||
if err != nil || c != n {
|
||||
return 0, err
|
||||
}
|
||||
// StackBottomMagic is the special address callers must past to all stack
|
||||
// marshalling operations to cause the src/dst address to be computed based on
|
||||
// the current end of the stack.
|
||||
const StackBottomMagic = ^usermem.Addr(0) // usermem.Addr(-1)
|
||||
|
||||
// CopyOutBytes implements marshal.CopyContext.CopyOutBytes. CopyOutBytes
|
||||
// computes an appropriate address based on the current end of the
|
||||
// stack. Callers use the sentinel address StackBottomMagic to marshal methods
|
||||
// to indicate this.
|
||||
func (s *Stack) CopyOutBytes(sentinel usermem.Addr, b []byte) (int, error) {
|
||||
if sentinel != StackBottomMagic {
|
||||
panic("Attempted to copy out to stack with absolute address")
|
||||
}
|
||||
c := len(b)
|
||||
n, err := s.IO.CopyOut(context.Background(), s.Bottom-usermem.Addr(c), b, usermem.IOOpts{})
|
||||
if err == nil && n == c {
|
||||
s.Bottom -= usermem.Addr(n)
|
||||
}
|
||||
|
||||
return s.Bottom, nil
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Pop pops the given values off the stack.
|
||||
//
|
||||
// (This method supports Addrs and treats them as native types.)
|
||||
func (s *Stack) Pop(vals ...interface{}) (usermem.Addr, error) {
|
||||
for _, v := range vals {
|
||||
|
||||
vaddr, isVaddr := v.(*usermem.Addr)
|
||||
|
||||
var n int
|
||||
var err error
|
||||
if isVaddr {
|
||||
value := s.Arch.Native(uintptr(0))
|
||||
n, err = usermem.CopyObjectIn(context.Background(), s.IO, s.Bottom, value, usermem.IOOpts{})
|
||||
*vaddr = usermem.Addr(s.Arch.Value(value))
|
||||
} else {
|
||||
n, err = usermem.CopyObjectIn(context.Background(), s.IO, s.Bottom, v, usermem.IOOpts{})
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// CopyInBytes implements marshal.CopyContext.CopyInBytes. CopyInBytes computes
|
||||
// an appropriate address based on the current end of the stack. Callers must
|
||||
// use the sentinel address StackBottomMagic to marshal methods to indicate
|
||||
// this.
|
||||
func (s *Stack) CopyInBytes(sentinel usermem.Addr, b []byte) (int, error) {
|
||||
if sentinel != StackBottomMagic {
|
||||
panic("Attempted to copy in from stack with absolute address")
|
||||
}
|
||||
n, err := s.IO.CopyIn(context.Background(), s.Bottom, b, usermem.IOOpts{})
|
||||
if err == nil {
|
||||
s.Bottom += usermem.Addr(n)
|
||||
}
|
||||
|
||||
return s.Bottom, nil
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Align aligns the stack to the given offset.
|
||||
@@ -142,6 +99,22 @@ func (s *Stack) Align(offset int) {
|
||||
}
|
||||
}
|
||||
|
||||
// PushNullTerminatedByteSlice writes bs to the stack, followed by an extra null
|
||||
// byte at the end. On error, the contents of the stack and the bottom cursor
|
||||
// are undefined.
|
||||
func (s *Stack) PushNullTerminatedByteSlice(bs []byte) (int, error) {
|
||||
// Note: Stack grows up, so write the terminal null byte first.
|
||||
nNull, err := primitive.CopyUint8Out(s, StackBottomMagic, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := primitive.CopyByteSliceOut(s, StackBottomMagic, bs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n + nNull, nil
|
||||
}
|
||||
|
||||
// StackLayout describes the location of the arguments and environment on the
|
||||
// stack.
|
||||
type StackLayout struct {
|
||||
@@ -177,11 +150,10 @@ func (s *Stack) Load(args []string, env []string, aux Auxv) (StackLayout, error)
|
||||
l.EnvvEnd = s.Bottom
|
||||
envAddrs := make([]usermem.Addr, len(env))
|
||||
for i := len(env) - 1; i >= 0; i-- {
|
||||
addr, err := s.Push(env[i])
|
||||
if err != nil {
|
||||
if _, err := s.PushNullTerminatedByteSlice([]byte(env[i])); err != nil {
|
||||
return StackLayout{}, err
|
||||
}
|
||||
envAddrs[i] = addr
|
||||
envAddrs[i] = s.Bottom
|
||||
}
|
||||
l.EnvvStart = s.Bottom
|
||||
|
||||
@@ -189,11 +161,10 @@ func (s *Stack) Load(args []string, env []string, aux Auxv) (StackLayout, error)
|
||||
l.ArgvEnd = s.Bottom
|
||||
argAddrs := make([]usermem.Addr, len(args))
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
addr, err := s.Push(args[i])
|
||||
if err != nil {
|
||||
if _, err := s.PushNullTerminatedByteSlice([]byte(args[i])); err != nil {
|
||||
return StackLayout{}, err
|
||||
}
|
||||
argAddrs[i] = addr
|
||||
argAddrs[i] = s.Bottom
|
||||
}
|
||||
l.ArgvStart = s.Bottom
|
||||
|
||||
@@ -222,26 +193,26 @@ func (s *Stack) Load(args []string, env []string, aux Auxv) (StackLayout, error)
|
||||
auxv = append(auxv, usermem.Addr(a.Key), a.Value)
|
||||
}
|
||||
auxv = append(auxv, usermem.Addr(0))
|
||||
_, err := s.Push(auxv)
|
||||
_, err := s.pushAddrSliceAndTerminator(auxv)
|
||||
if err != nil {
|
||||
return StackLayout{}, err
|
||||
}
|
||||
|
||||
// Push environment.
|
||||
_, err = s.Push(envAddrs)
|
||||
_, err = s.pushAddrSliceAndTerminator(envAddrs)
|
||||
if err != nil {
|
||||
return StackLayout{}, err
|
||||
}
|
||||
|
||||
// Push args.
|
||||
_, err = s.Push(argAddrs)
|
||||
_, err = s.pushAddrSliceAndTerminator(argAddrs)
|
||||
if err != nil {
|
||||
return StackLayout{}, err
|
||||
}
|
||||
|
||||
// Push arg count.
|
||||
_, err = s.Push(usermem.Addr(len(args)))
|
||||
if err != nil {
|
||||
lenP := s.Arch.Native(uintptr(len(args)))
|
||||
if _, err = lenP.CopyOut(s, StackBottomMagic); err != nil {
|
||||
return StackLayout{}, err
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2020 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 arch
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// pushAddrSliceAndTerminator copies a slices of addresses to the stack, and
|
||||
// also pushes an extra null address element at the end of the slice.
|
||||
//
|
||||
// Internally, we unsafely transmute the slice type from the arch-dependent
|
||||
// []usermem.Addr type, to a slice of fixed-sized ints so that we can pass it to
|
||||
// go-marshal.
|
||||
//
|
||||
// On error, the contents of the stack and the bottom cursor are undefined.
|
||||
func (s *Stack) pushAddrSliceAndTerminator(src []usermem.Addr) (int, error) {
|
||||
// Note: Stack grows upwards, so push the terminator first.
|
||||
srcHdr := (*reflect.SliceHeader)(unsafe.Pointer(&src))
|
||||
switch s.Arch.Width() {
|
||||
case 8:
|
||||
nNull, err := primitive.CopyUint64Out(s, StackBottomMagic, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var dst []uint64
|
||||
dstHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
|
||||
dstHdr.Data = srcHdr.Data
|
||||
dstHdr.Len = srcHdr.Len
|
||||
dstHdr.Cap = srcHdr.Cap
|
||||
n, err := primitive.CopyUint64SliceOut(s, StackBottomMagic, dst)
|
||||
// Ensures src doesn't get GCed until we're done using it through dst.
|
||||
runtime.KeepAlive(src)
|
||||
return n + nNull, err
|
||||
case 4:
|
||||
nNull, err := primitive.CopyUint32Out(s, StackBottomMagic, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var dst []uint32
|
||||
dstHdr := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
|
||||
dstHdr.Data = srcHdr.Data
|
||||
dstHdr.Len = srcHdr.Len
|
||||
dstHdr.Cap = srcHdr.Cap
|
||||
n, err := primitive.CopyUint32SliceOut(s, StackBottomMagic, dst)
|
||||
// Ensure src doesn't get GCed until we're done using it through dst.
|
||||
runtime.KeepAlive(src)
|
||||
return n + nNull, err
|
||||
default:
|
||||
panic("Unsupported arch width")
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ go_library(
|
||||
"//pkg/amutex",
|
||||
"//pkg/buffer",
|
||||
"//pkg/context",
|
||||
"//pkg/marshal/primitive",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/device",
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/amutex"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
@@ -145,9 +146,14 @@ func (p *Pipe) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArgume
|
||||
v = math.MaxInt32 // Silently truncate.
|
||||
}
|
||||
// Copy result to userspace.
|
||||
_, err := usermem.CopyObjectOut(ctx, io, args[2].Pointer(), int32(v), usermem.IOOpts{
|
||||
AddressSpaceActive: true,
|
||||
})
|
||||
iocc := primitive.IOCopyContext{
|
||||
IO: io,
|
||||
Ctx: ctx,
|
||||
Opts: usermem.IOOpts{
|
||||
AddressSpaceActive: true,
|
||||
},
|
||||
}
|
||||
_, err := primitive.CopyInt32Out(&iocc, args[2].Pointer(), int32(v))
|
||||
return 0, err
|
||||
default:
|
||||
return 0, syscall.ENOTTY
|
||||
|
||||
@@ -126,7 +126,11 @@ func (t *Task) SyscallTable() *SyscallTable {
|
||||
// Preconditions: The caller must be running on the task goroutine, or t.mu
|
||||
// must be locked.
|
||||
func (t *Task) Stack() *arch.Stack {
|
||||
return &arch.Stack{t.Arch(), t.MemoryManager(), usermem.Addr(t.Arch().Stack())}
|
||||
return &arch.Stack{
|
||||
Arch: t.Arch(),
|
||||
IO: t.MemoryManager(),
|
||||
Bottom: usermem.Addr(t.Arch().Stack()),
|
||||
}
|
||||
}
|
||||
|
||||
// LoadTaskImage loads a specified file into a new TaskContext.
|
||||
|
||||
@@ -259,7 +259,11 @@ func (t *Task) deliverSignalToHandler(info *arch.SignalInfo, act arch.SignalAct)
|
||||
// Set up the signal handler. If we have a saved signal mask, the signal
|
||||
// handler should run with the current mask, but sigreturn should restore
|
||||
// the saved one.
|
||||
st := &arch.Stack{t.Arch(), mm, sp}
|
||||
st := &arch.Stack{
|
||||
Arch: t.Arch(),
|
||||
IO: mm,
|
||||
Bottom: sp,
|
||||
}
|
||||
mask := t.signalMask
|
||||
if t.haveSavedSignalMask {
|
||||
mask = t.savedSignalMask
|
||||
|
||||
@@ -122,7 +122,7 @@ func allocStack(ctx context.Context, m *mm.MemoryManager, a arch.Context) (*arch
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &arch.Stack{a, m, ar.End}, nil
|
||||
return &arch.Stack{Arch: a, IO: m, Bottom: ar.End}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -247,20 +247,20 @@ func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *V
|
||||
}
|
||||
|
||||
// Push the original filename to the stack, for AT_EXECFN.
|
||||
execfn, err := stack.Push(args.Filename)
|
||||
if err != nil {
|
||||
if _, err := stack.PushNullTerminatedByteSlice([]byte(args.Filename)); err != nil {
|
||||
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push exec filename: %v", err), syserr.FromError(err).ToLinux())
|
||||
}
|
||||
execfn := stack.Bottom
|
||||
|
||||
// Push 16 random bytes on the stack which AT_RANDOM will point to.
|
||||
var b [16]byte
|
||||
if _, err := rand.Read(b[:]); err != nil {
|
||||
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to read random bytes: %v", err), syserr.FromError(err).ToLinux())
|
||||
}
|
||||
random, err := stack.Push(b)
|
||||
if err != nil {
|
||||
if _, err = stack.PushNullTerminatedByteSlice(b[:]); err != nil {
|
||||
return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push random bytes: %v", err), syserr.FromError(err).ToLinux())
|
||||
}
|
||||
random := stack.Bottom
|
||||
|
||||
c := auth.CredentialsFromContext(ctx)
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package strace
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -294,7 +293,7 @@ func itimerval(t *kernel.Task, addr usermem.Addr) string {
|
||||
}
|
||||
|
||||
interval := timeval(t, addr)
|
||||
value := timeval(t, addr+usermem.Addr(binary.Size(linux.Timeval{})))
|
||||
value := timeval(t, addr+usermem.Addr((*linux.Timeval)(nil).SizeBytes()))
|
||||
return fmt.Sprintf("%#x {interval=%s, value=%s}", addr, interval, value)
|
||||
}
|
||||
|
||||
@@ -304,7 +303,7 @@ func itimerspec(t *kernel.Task, addr usermem.Addr) string {
|
||||
}
|
||||
|
||||
interval := timespec(t, addr)
|
||||
value := timespec(t, addr+usermem.Addr(binary.Size(linux.Timespec{})))
|
||||
value := timespec(t, addr+usermem.Addr((*linux.Timespec)(nil).SizeBytes()))
|
||||
return fmt.Sprintf("%#x {interval=%s, value=%s}", addr, interval, value)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/binary"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/gohacks"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
@@ -184,51 +183,6 @@ func (rw *IOReadWriter) Write(src []byte) (int, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// CopyObjectOut copies a fixed-size value or slice of fixed-size values from
|
||||
// src to the memory mapped at addr in uio. It returns the number of bytes
|
||||
// copied.
|
||||
//
|
||||
// CopyObjectOut must use reflection to encode src; performance-sensitive
|
||||
// clients should do encoding manually and use uio.CopyOut directly.
|
||||
//
|
||||
// Preconditions: Same as IO.CopyOut.
|
||||
func CopyObjectOut(ctx context.Context, uio IO, addr Addr, src interface{}, opts IOOpts) (int, error) {
|
||||
w := &IOReadWriter{
|
||||
Ctx: ctx,
|
||||
IO: uio,
|
||||
Addr: addr,
|
||||
Opts: opts,
|
||||
}
|
||||
// Allocate a byte slice the size of the object being marshaled. This
|
||||
// adds an extra reflection call, but avoids needing to grow the slice
|
||||
// during encoding, which can result in many heap-allocated slices.
|
||||
b := make([]byte, 0, binary.Size(src))
|
||||
return w.Write(binary.Marshal(b, ByteOrder, src))
|
||||
}
|
||||
|
||||
// CopyObjectIn copies a fixed-size value or slice of fixed-size values from
|
||||
// the memory mapped at addr in uio to dst. It returns the number of bytes
|
||||
// copied.
|
||||
//
|
||||
// CopyObjectIn must use reflection to decode dst; performance-sensitive
|
||||
// clients should use uio.CopyIn directly and do decoding manually.
|
||||
//
|
||||
// Preconditions: Same as IO.CopyIn.
|
||||
func CopyObjectIn(ctx context.Context, uio IO, addr Addr, dst interface{}, opts IOOpts) (int, error) {
|
||||
r := &IOReadWriter{
|
||||
Ctx: ctx,
|
||||
IO: uio,
|
||||
Addr: addr,
|
||||
Opts: opts,
|
||||
}
|
||||
buf := make([]byte, binary.Size(dst))
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
binary.Unmarshal(buf, ByteOrder, dst)
|
||||
return int(r.Addr - addr), nil
|
||||
}
|
||||
|
||||
// CopyStringIn tuning parameters, defined outside that function for tests.
|
||||
const (
|
||||
copyStringIncrement = 64
|
||||
|
||||
@@ -16,7 +16,6 @@ package usermem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -174,23 +173,6 @@ type testStruct struct {
|
||||
Uint64 uint64
|
||||
}
|
||||
|
||||
func TestCopyObject(t *testing.T) {
|
||||
wantObj := testStruct{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
wantN := binary.Size(wantObj)
|
||||
b := &BytesIO{make([]byte, wantN)}
|
||||
ctx := newContext()
|
||||
if n, err := CopyObjectOut(ctx, b, 0, &wantObj, IOOpts{}); n != wantN || err != nil {
|
||||
t.Fatalf("CopyObjectOut: got (%v, %v), wanted (%v, nil)", n, err, wantN)
|
||||
}
|
||||
var gotObj testStruct
|
||||
if n, err := CopyObjectIn(ctx, b, 0, &gotObj, IOOpts{}); n != wantN || err != nil {
|
||||
t.Errorf("CopyObjectIn: got (%v, %v), wanted (%v, nil)", n, err, wantN)
|
||||
}
|
||||
if gotObj != wantObj {
|
||||
t.Errorf("CopyObject round trip: got %+v, wanted %+v", gotObj, wantObj)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyStringInShort(t *testing.T) {
|
||||
// Tests for string length <= copyStringIncrement.
|
||||
want := strings.Repeat("A", copyStringIncrement-2)
|
||||
|
||||
Reference in New Issue
Block a user