usermem

package
v0.0.0-...-23e6066 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2018 License: Apache-2.0 Imports: 15 Imported by: 0

README

This package defines primitives for sentry access to application memory.

Major types:

  • The IO interface represents a virtual address space and provides I/O methods on that address space. IO is the lowest-level primitive. The primary implementation of the IO interface is mm.MemoryManager.

  • IOSequence represents a collection of individually-contiguous address ranges in a IO that is operated on sequentially, analogous to Linux's struct iov_iter.

Major usage patterns:

  • Access to a task's virtual memory, subject to the application's memory protections and while running on that task's goroutine, from a context that is at or above the level of the kernel package (e.g. most syscall implementations in syscalls/linux); use the kernel.Task.Copy* wrappers defined in kernel/task_usermem.go.

  • Access to a task's virtual memory, from a context that is at or above the level of the kernel package, but where any of the above constraints does not hold (e.g. PTRACE_POKEDATA, which ignores application memory protections); obtain the task's mm.MemoryManager by calling kernel.Task.MemoryManager, and call its IO methods directly.

  • Access to a task's virtual memory, from a context that is below the level of the kernel package (e.g. filesystem I/O); clients must pass I/O arguments from higher layers, usually in the form of an IOSequence. The kernel.Task.SingleIOSequence and kernel.Task.IovecsIOSequence functions in kernel/task_usermem.go are convenience functions for doing so.

Documentation

Overview

Package usermem governs access to user memory.

Index

Constants

View Source
const (
	// PageSize is the system page size.
	PageSize = 1 << PageShift

	// HugePageSize is the system huge page size.
	HugePageSize = 1 << HugePageShift

	// PageShift is the binary log of the system page size.
	PageShift = 12

	// HugePageShift is the binary log of the system huge page size.
	HugePageShift = 21
)

Variables

View Source
var (
	NoAccess  = AccessType{}
	Read      = AccessType{Read: true}
	Write     = AccessType{Write: true}
	Execute   = AccessType{Execute: true}
	ReadWrite = AccessType{Read: true, Write: true}
	AnyAccess = AccessType{Read: true, Write: true, Execute: true}
)

Convenient access types.

View Source
var (
	// ByteOrder is the native byte order (little endian).
	ByteOrder = binary.LittleEndian
)
View Source
var ErrEndOfIOSequence = errors.New("write beyond end of IOSequence")

ErrEndOfIOSequence is returned by IOSequence.Writer().Write() when attempting to write beyond the end of the IOSequence.

Functions

func CopyInVec

func CopyInVec(ctx context.Context, uio IO, ars AddrRangeSeq, dst []byte, opts IOOpts) (int, error)

CopyInVec copies bytes from the memory mapped at ars in uio to dst. The maximum number of bytes copied is ars.NumBytes() or len(dst), whichever is less. CopyInVec returns the number of bytes copied; if this is less than the maximum, it returns a non-nil error explaining why.

Preconditions: As for IO.CopyIn.

func CopyInt32StringInVec

func CopyInt32StringInVec(ctx context.Context, uio IO, ars AddrRangeSeq, dst *int32, opts IOOpts) (int64, error)

CopyInt32StringInVec is equivalent to CopyInt32StringsInVec, but copies at most one int32.

func CopyInt32StringsInVec

func CopyInt32StringsInVec(ctx context.Context, uio IO, ars AddrRangeSeq, dsts []int32, opts IOOpts) (int64, error)

CopyInt32StringsInVec copies up to len(dsts) whitespace-separated decimal strings from the memory mapped at ars in uio and converts them to int32 values in dsts. It returns the number of bytes read.

CopyInt32StringsInVec shares the following properties with Linux's kernel/sysctl.c:proc_dointvec(write=1):

- If any read value overflows the range of int32, or any invalid characters are encountered during the read, CopyInt32StringsInVec returns EINVAL.

- If, upon reaching the end of ars, fewer than len(dsts) values have been read, CopyInt32StringsInVec returns no error if at least 1 value was read and EINVAL otherwise.

- Trailing whitespace after the last successfully read value is counted in the number of bytes read.

Unlike proc_dointvec():

- CopyInt32StringsInVec does not implicitly limit ars.NumBytes() to PageSize-1; callers that require this must do so explicitly.

- CopyInt32StringsInVec returns EINVAL if ars.NumBytes() == 0.

Preconditions: As for CopyInVec.

func CopyObjectIn

func CopyObjectIn(ctx context.Context, uio IO, addr Addr, dst interface{}, opts IOOpts) (int, error)

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: As for IO.CopyIn.

func CopyObjectOut

func CopyObjectOut(ctx context.Context, uio IO, addr Addr, src interface{}, opts IOOpts) (int, error)

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: As for IO.CopyOut.

func CopyOutVec

func CopyOutVec(ctx context.Context, uio IO, ars AddrRangeSeq, src []byte, opts IOOpts) (int, error)

CopyOutVec copies bytes from src to the memory mapped at ars in uio. The maximum number of bytes copied is ars.NumBytes() or len(src), whichever is less. CopyOutVec returns the number of bytes copied; if this is less than the maximum, it returns a non-nil error explaining why.

Preconditions: As for IO.CopyOut.

func CopyStringIn

func CopyStringIn(ctx context.Context, uio IO, addr Addr, maxlen int, opts IOOpts) (string, error)

CopyStringIn copies a NUL-terminated string of unknown length from the memory mapped at addr in uio and returns it as a string (not including the trailing NUL). If the length of the string, including the terminating NUL, would exceed maxlen, CopyStringIn returns the string truncated to maxlen and ENAMETOOLONG.

Preconditions: As for IO.CopyFromUser. maxlen >= 0.

func ZeroOutVec

func ZeroOutVec(ctx context.Context, uio IO, ars AddrRangeSeq, toZero int64, opts IOOpts) (int64, error)

ZeroOutVec writes zeroes to the memory mapped at ars in uio. The maximum number of bytes written is ars.NumBytes() or toZero, whichever is less. ZeroOutVec returns the number of bytes written; if this is less than the maximum, it returns a non-nil error explaining why.

Preconditions: As for IO.ZeroOut.

Types

type AccessType

type AccessType struct {
	// Read is read access.
	Read bool

	// Write is write access.
	Write bool

	// Execute is executable access.
	Execute bool
}

AccessType specifies memory access types. This is used for setting mapping permissions, as well as communicating faults.

func (AccessType) Any

func (a AccessType) Any() bool

Any returns true iff at least one of Read, Write or Execute is true.

func (AccessType) Effective

func (a AccessType) Effective() AccessType

Effective returns the set of effective access types allowed by a, even if some types are not explicitly allowed.

func (AccessType) Intersect

func (a AccessType) Intersect(other AccessType) AccessType

Intersect returns the access types set in both a and other.

func (AccessType) Prot

func (a AccessType) Prot() int

Prot returns the system prot (syscall.PROT_READ, etc.) for this access.

func (AccessType) String

func (a AccessType) String() string

String returns a pretty representation of access. This looks like the familiar r-x, rw-, etc. and can be relied on as such.

func (AccessType) SupersetOf

func (a AccessType) SupersetOf(other AccessType) bool

SupersetOf returns true iff the access types in a are a superset of the access types in other.

type Addr

type Addr uintptr

Addr represents a generic virtual address.

func (Addr) AddLength

func (v Addr) AddLength(length uint64) (end Addr, ok bool)

AddLength adds the given length to start and returns the result. ok is true iff adding the length did not overflow the range of Addr.

Note: This function is usually used to get the end of an address range defined by its start address and length. Since the resulting end is exclusive, end == 0 is technically valid, and corresponds to a range that extends to the end of the address space, but ok will be false. This isn't expected to ever come up in practice.

func (Addr) HugeRoundDown

func (v Addr) HugeRoundDown() Addr

HugeRoundDown returns the address rounded down to the nearest huge page boundary.

func (Addr) HugeRoundUp

func (v Addr) HugeRoundUp() (addr Addr, ok bool)

HugeRoundUp returns the address rounded up to the nearest huge page boundary. ok is true iff rounding up did not wrap around.

func (Addr) IsPageAligned

func (v Addr) IsPageAligned() bool

IsPageAligned returns true if v.PageOffset() == 0.

func (Addr) MustRoundUp

func (v Addr) MustRoundUp() Addr

MustRoundUp is equivalent to RoundUp, but panics if rounding up wraps around.

func (Addr) PageOffset

func (v Addr) PageOffset() uint64

PageOffset returns the offset of v into the current page.

func (Addr) RoundDown

func (v Addr) RoundDown() Addr

RoundDown returns the address rounded down to the nearest page boundary.

func (Addr) RoundUp

func (v Addr) RoundUp() (addr Addr, ok bool)

RoundUp returns the address rounded up to the nearest page boundary. ok is true iff rounding up did not wrap around.

func (Addr) ToRange

func (v Addr) ToRange(length uint64) (AddrRange, bool)

ToRange returns [v, v+length).

type AddrRangeSeq

type AddrRangeSeq struct {
	// contains filtered or unexported fields
}

An AddrRangeSeq represents a sequence of AddrRanges.

AddrRangeSeqs are immutable and may be copied by value. The zero value of AddrRangeSeq represents an empty sequence.

An AddrRangeSeq may contain AddrRanges with a length of 0. This is necessary since zero-length AddrRanges are significant to MM bounds checks.

func AddrRangeSeqFromSlice

func AddrRangeSeqFromSlice(slice []AddrRange) AddrRangeSeq

AddrRangeSeqFromSlice returns an AddrRangeSeq representing all AddrRanges in slice.

Whether the returned AddrRangeSeq shares memory with slice is unspecified; clients should avoid mutating slices passed to AddrRangeSeqFromSlice.

Preconditions: The combined length of all AddrRanges in slice <= math.MaxInt64.

func AddrRangeSeqOf

func AddrRangeSeqOf(ar AddrRange) AddrRangeSeq

AddrRangeSeqOf returns an AddrRangeSeq representing the single AddrRange ar.

func (AddrRangeSeq) DropFirst

func (ars AddrRangeSeq) DropFirst(n int) AddrRangeSeq

DropFirst returns an AddrRangeSeq equivalent to ars, but with the first n bytes omitted. If n > ars.NumBytes(), DropFirst returns an empty AddrRangeSeq.

If !ars.IsEmpty() and ars.Head().Length() == 0, DropFirst will always omit at least ars.Head(), even if n == 0. This guarantees that the basic pattern of:

for !ars.IsEmpty() {
  n, err = doIOWith(ars.Head())
  if err != nil {
    return err
  }
  ars = ars.DropFirst(n)
}

works even in the presence of zero-length AddrRanges.

Preconditions: n >= 0.

func (AddrRangeSeq) DropFirst64

func (ars AddrRangeSeq) DropFirst64(n int64) AddrRangeSeq

DropFirst64 is equivalent to DropFirst but takes an int64.

func (AddrRangeSeq) Head

func (ars AddrRangeSeq) Head() AddrRange

Head returns the first AddrRange in ars.

Preconditions: !ars.IsEmpty().

func (AddrRangeSeq) IsEmpty

func (ars AddrRangeSeq) IsEmpty() bool

IsEmpty returns true if ars.NumRanges() == 0.

Note that since AddrRangeSeq may contain AddrRanges with a length of zero, an AddrRange representing 0 bytes (AddrRangeSeq.NumBytes() == 0) is not necessarily empty.

func (AddrRangeSeq) NumBytes

func (ars AddrRangeSeq) NumBytes() int64

NumBytes returns the number of bytes represented by ars.

func (AddrRangeSeq) NumRanges

func (ars AddrRangeSeq) NumRanges() int

NumRanges returns the number of AddrRanges in ars.

func (AddrRangeSeq) String

func (ars AddrRangeSeq) String() string

String implements fmt.Stringer.String.

func (AddrRangeSeq) Tail

func (ars AddrRangeSeq) Tail() AddrRangeSeq

Tail returns an AddrRangeSeq consisting of all AddrRanges in ars after the first.

Preconditions: !ars.IsEmpty().

func (AddrRangeSeq) TakeFirst

func (ars AddrRangeSeq) TakeFirst(n int) AddrRangeSeq

TakeFirst returns an AddrRangeSeq equivalent to ars, but iterating at most n bytes. TakeFirst never removes AddrRanges from ars; AddrRanges beyond the first n bytes are reduced to a length of zero, but will still be iterated.

Preconditions: n >= 0.

func (AddrRangeSeq) TakeFirst64

func (ars AddrRangeSeq) TakeFirst64(n int64) AddrRangeSeq

TakeFirst64 is equivalent to TakeFirst but takes an int64.

type BytesIO

type BytesIO struct {
	Bytes []byte
}

BytesIO implements IO using a byte slice. Addresses are interpreted as offsets into the slice. Reads and writes beyond the end of the slice return EFAULT.

func (*BytesIO) CompareAndSwapUint32

func (b *BytesIO) CompareAndSwapUint32(ctx context.Context, addr Addr, old, new uint32, opts IOOpts) (uint32, error)

CompareAndSwapUint32 implements IO.CompareAndSwapUint32.

func (*BytesIO) CopyIn

func (b *BytesIO) CopyIn(ctx context.Context, addr Addr, dst []byte, opts IOOpts) (int, error)

CopyIn implements IO.CopyIn.

func (*BytesIO) CopyInTo

func (b *BytesIO) CopyInTo(ctx context.Context, ars AddrRangeSeq, dst safemem.Writer, opts IOOpts) (int64, error)

CopyInTo implements IO.CopyInTo.

func (*BytesIO) CopyOut

func (b *BytesIO) CopyOut(ctx context.Context, addr Addr, src []byte, opts IOOpts) (int, error)

CopyOut implements IO.CopyOut.

func (*BytesIO) CopyOutFrom

func (b *BytesIO) CopyOutFrom(ctx context.Context, ars AddrRangeSeq, src safemem.Reader, opts IOOpts) (int64, error)

CopyOutFrom implements IO.CopyOutFrom.

func (*BytesIO) SwapUint32

func (b *BytesIO) SwapUint32(ctx context.Context, addr Addr, new uint32, opts IOOpts) (uint32, error)

SwapUint32 implements IO.SwapUint32.

func (*BytesIO) ZeroOut

func (b *BytesIO) ZeroOut(ctx context.Context, addr Addr, toZero int64, opts IOOpts) (int64, error)

ZeroOut implements IO.ZeroOut.

type IO

type IO interface {
	// CopyOut copies len(src) bytes from src to the memory mapped at addr. It
	// returns the number of bytes copied. If the number of bytes copied is <
	// len(src), it returns a non-nil error explaining why.
	//
	// Preconditions: The caller must not hold mm.MemoryManager.mappingMu or
	// any following locks in the lock order.
	CopyOut(ctx context.Context, addr Addr, src []byte, opts IOOpts) (int, error)

	// CopyIn copies len(dst) bytes from the memory mapped at addr to dst.
	// It returns the number of bytes copied. If the number of bytes copied is
	// < len(dst), it returns a non-nil error explaining why.
	//
	// Preconditions: The caller must not hold mm.MemoryManager.mappingMu or
	// any following locks in the lock order.
	CopyIn(ctx context.Context, addr Addr, dst []byte, opts IOOpts) (int, error)

	// ZeroOut sets toZero bytes to 0, starting at addr. It returns the number
	// of bytes zeroed. If the number of bytes zeroed is < toZero, it returns a
	// non-nil error explaining why.
	//
	// Preconditions: The caller must not hold mm.MemoryManager.mappingMu or
	// any following locks in the lock order. toZero >= 0.
	ZeroOut(ctx context.Context, addr Addr, toZero int64, opts IOOpts) (int64, error)

	// CopyOutFrom copies ars.NumBytes() bytes from src to the memory mapped at
	// ars. It returns the number of bytes copied, which may be less than the
	// number of bytes read from src if copying fails. CopyOutFrom may return a
	// partial copy without an error iff src.ReadToBlocks returns a partial
	// read without an error.
	//
	// CopyOutFrom calls src.ReadToBlocks at most once.
	//
	// Preconditions: The caller must not hold mm.MemoryManager.mappingMu or
	// any following locks in the lock order. src.ReadToBlocks must not block
	// on mm.MemoryManager.activeMu or any preceding locks in the lock order.
	CopyOutFrom(ctx context.Context, ars AddrRangeSeq, src safemem.Reader, opts IOOpts) (int64, error)

	// CopyInTo copies ars.NumBytes() bytes from the memory mapped at ars to
	// dst. It returns the number of bytes copied. CopyInTo may return a
	// partial copy without an error iff dst.WriteFromBlocks returns a partial
	// write without an error.
	//
	// CopyInTo calls dst.WriteFromBlocks at most once.
	//
	// Preconditions: The caller must not hold mm.MemoryManager.mappingMu or
	// any following locks in the lock order. dst.WriteFromBlocks must not
	// block on mm.MemoryManager.activeMu or any preceding locks in the lock
	// order.
	CopyInTo(ctx context.Context, ars AddrRangeSeq, dst safemem.Writer, opts IOOpts) (int64, error)

	// SwapUint32 atomically sets the uint32 value at addr to new and
	// returns the previous value.
	//
	// Preconditions: The caller must not hold mm.MemoryManager.mappingMu or
	// any following locks in the lock order. addr must be aligned to a 4-byte
	// boundary.
	SwapUint32(ctx context.Context, addr Addr, new uint32, opts IOOpts) (uint32, error)

	// CompareAndSwapUint32 atomically compares the uint32 value at addr to
	// old; if they are equal, the value in memory is replaced by new. In
	// either case, the previous value stored in memory is returned.
	//
	// Preconditions: The caller must not hold mm.MemoryManager.mappingMu or
	// any following locks in the lock order. addr must be aligned to a 4-byte
	// boundary.
	CompareAndSwapUint32(ctx context.Context, addr Addr, old, new uint32, opts IOOpts) (uint32, error)
}

IO provides access to the contents of a virtual memory space.

FIXME: Implementations of IO cannot expect ctx to contain any meaningful data.

type IOOpts

type IOOpts struct {
	// If IgnorePermissions is true, application-defined memory protections set
	// by mmap(2) or mprotect(2) will be ignored. (Memory protections required
	// by the target of the mapping are never ignored.)
	IgnorePermissions bool

	// If AddressSpaceActive is true, the IO implementation may assume that it
	// has an active AddressSpace and can therefore use AddressSpace copying
	// without performing activation. See mm/io.go for details.
	AddressSpaceActive bool
}

IOOpts contains options applicable to all IO methods.

type IOReadWriter

type IOReadWriter struct {
	Ctx  context.Context
	IO   IO
	Addr Addr
	Opts IOOpts
}

IOReadWriter is an io.ReadWriter that reads from / writes to addresses starting at addr in IO. The preconditions that apply to IO.CopyIn and IO.CopyOut also apply to IOReadWriter.Read and IOReadWriter.Write respectively.

func (*IOReadWriter) Read

func (rw *IOReadWriter) Read(dst []byte) (int, error)

Read implements io.Reader.Read.

Note that an address space does not have an "end of file", so Read can only return io.EOF if IO.CopyIn returns io.EOF. Attempts to read unmapped or unreadable memory, or beyond the end of the address space, should return EFAULT.

func (*IOReadWriter) Write

func (rw *IOReadWriter) Write(src []byte) (int, error)

Writer implements io.Writer.Write.

type IOSequence

type IOSequence struct {
	IO    IO
	Addrs AddrRangeSeq
	Opts  IOOpts
}

IOSequence holds arguments to IO methods.

func BytesIOSequence

func BytesIOSequence(buf []byte) IOSequence

BytesIOSequence returns an IOSequence representing the given byte slice.

func (IOSequence) CopyIn

func (s IOSequence) CopyIn(ctx context.Context, dst []byte) (int, error)

CopyIn invokes CopyInVec over s.Addrs.

As with CopyInVec, if s.NumBytes() < len(dst), the copy will be truncated to s.NumBytes(), and a nil error will be returned.

Preconditions: As for CopyInVec.

func (IOSequence) CopyInTo

func (s IOSequence) CopyInTo(ctx context.Context, dst safemem.Writer) (int64, error)

CopyInTo invokes s.CopyInTo over s.Addrs.

Preconditions: As for IO.CopyInTo.

func (IOSequence) CopyOut

func (s IOSequence) CopyOut(ctx context.Context, src []byte) (int, error)

CopyOut invokes CopyOutVec over s.Addrs.

As with CopyOutVec, if s.NumBytes() < len(src), the copy will be truncated to s.NumBytes(), and a nil error will be returned.

Preconditions: As for CopyOutVec.

func (IOSequence) CopyOutFrom

func (s IOSequence) CopyOutFrom(ctx context.Context, src safemem.Reader) (int64, error)

CopyOutFrom invokes s.CopyOutFrom over s.Addrs.

Preconditions: As for IO.CopyOutFrom.

func (IOSequence) DropFirst

func (s IOSequence) DropFirst(n int) IOSequence

DropFirst returns a copy of s with s.Addrs.DropFirst(n).

Preconditions: As for AddrRangeSeq.DropFirst.

func (IOSequence) DropFirst64

func (s IOSequence) DropFirst64(n int64) IOSequence

DropFirst64 returns a copy of s with s.Addrs.DropFirst64(n).

Preconditions: As for AddrRangeSeq.DropFirst64.

func (IOSequence) NumBytes

func (s IOSequence) NumBytes() int64

NumBytes returns s.Addrs.NumBytes().

Note that NumBytes() may return 0 even if !s.Addrs.IsEmpty(), since s.Addrs may contain a non-zero number of zero-length AddrRanges. Many clients of IOSequence currently do something like:

if ioseq.NumBytes() == 0 {
  return 0, nil
}
if f.availableBytes == 0 {
  return 0, syserror.ErrWouldBlock
}
return ioseq.CopyOutFrom(..., reader)

In such cases, using s.Addrs.IsEmpty() will cause them to have the wrong behavior for zero-length I/O. However, using s.NumBytes() == 0 instead means that we will return success for zero-length I/O in cases where Linux would return EFAULT due to a failed access_ok() check, so in the long term we should move checks for ErrWouldBlock etc. into the body of reader.ReadToBlocks and use s.Addrs.IsEmpty() instead.

func (IOSequence) Reader

func (s IOSequence) Reader(ctx context.Context) io.Reader

Reader returns an io.Reader that reads from s. Reads beyond the end of s return io.EOF. The preconditions that apply to s.CopyIn also apply to the returned io.Reader.Read.

func (IOSequence) TakeFirst

func (s IOSequence) TakeFirst(n int) IOSequence

TakeFirst returns a copy of s with s.Addrs.TakeFirst(n).

Preconditions: As for AddrRangeSeq.TakeFirst.

func (IOSequence) TakeFirst64

func (s IOSequence) TakeFirst64(n int64) IOSequence

TakeFirst64 returns a copy of s with s.Addrs.TakeFirst64(n).

Preconditions: As for AddrRangeSeq.TakeFirst64.

func (IOSequence) Writer

func (s IOSequence) Writer(ctx context.Context) io.Writer

Writer returns an io.Writer that writes to s. Writes beyond the end of s return ErrEndOfIOSequence. The preconditions that apply to s.CopyOut also apply to the returned io.Writer.Write.

func (IOSequence) ZeroOut

func (s IOSequence) ZeroOut(ctx context.Context, toZero int64) (int64, error)

ZeroOut invokes ZeroOutVec over s.Addrs.

As with ZeroOutVec, if s.NumBytes() < toZero, the write will be truncated to s.NumBytes(), and a nil error will be returned.

Preconditions: As for ZeroOutVec.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL