usermem

package
v0.0.0-...-0f8db9f Latest Latest
Warning

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

Go to latest
Published: Dec 20, 2024 License: Apache-2.0, MIT Imports: 12 Imported by: 122

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

This section is empty.

Variables

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 hostarch.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: Same as IO.CopyIn.

func CopyInt32StringInVec

func CopyInt32StringInVec(ctx context.Context, uio IO, ars hostarch.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 hostarch.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: Same as CopyInVec.

func CopyObjectIn

func CopyObjectIn(ctx context.Context, uio IO, addr hostarch.Addr, dst any, 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: Same as IO.CopyIn.

func CopyObjectOut

func CopyObjectOut(ctx context.Context, uio IO, addr hostarch.Addr, src any, 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: Same as IO.CopyOut.

func CopyOutVec

func CopyOutVec(ctx context.Context, uio IO, ars hostarch.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: Same as IO.CopyOut.

func CopyStringIn

func CopyStringIn(ctx context.Context, uio IO, addr hostarch.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: Same as IO.CopyFromUser, plus:

  • maxlen >= 0.

func ZeroOutVec

func ZeroOutVec(ctx context.Context, uio IO, ars hostarch.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: Same as IO.ZeroOut.

Types

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 hostarch.Addr, old, new uint32, opts IOOpts) (uint32, error)

CompareAndSwapUint32 implements IO.CompareAndSwapUint32.

func (*BytesIO) CopyIn

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

CopyIn implements IO.CopyIn.

func (*BytesIO) CopyInTo

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

CopyInTo implements IO.CopyInTo.

func (*BytesIO) CopyOut

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

CopyOut implements IO.CopyOut.

func (*BytesIO) CopyOutFrom

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

CopyOutFrom implements IO.CopyOutFrom.

func (*BytesIO) LoadUint32

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

LoadUint32 implements IO.LoadUint32.

func (*BytesIO) SwapUint32

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

SwapUint32 implements IO.SwapUint32.

func (*BytesIO) ZeroOut

func (b *BytesIO) ZeroOut(ctx context.Context, addr hostarch.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.
	//
	// Postconditions: CopyOut does not retain src.
	CopyOut(ctx context.Context, addr hostarch.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.
	//
	// Postconditions: CopyIn does not retain dst.
	CopyIn(ctx context.Context, addr hostarch.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 hostarch.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 hostarch.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 hostarch.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 hostarch.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 hostarch.Addr, old, new uint32, opts IOOpts) (uint32, error)

	// LoadUint32 atomically loads the uint32 value at addr and returns it.
	//
	// 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.
	LoadUint32(ctx context.Context, addr hostarch.Addr, opts IOOpts) (uint32, error)
}

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

type IOCopyContext

type IOCopyContext struct {
	Ctx  context.Context
	IO   IO
	Opts IOOpts
}

IOCopyContext wraps an object implementing hostarch.IO to implement marshal.CopyContext.

func (*IOCopyContext) CopyInBytes

func (i *IOCopyContext) CopyInBytes(addr hostarch.Addr, b []byte) (int, error)

CopyInBytes implements marshal.CopyContext.CopyInBytes.

func (*IOCopyContext) CopyOutBytes

func (i *IOCopyContext) CopyOutBytes(addr hostarch.Addr, b []byte) (int, error)

CopyOutBytes implements marshal.CopyContext.CopyOutBytes.

func (*IOCopyContext) CopyScratchBuffer

func (i *IOCopyContext) CopyScratchBuffer(size int) []byte

CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer.

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 hostarch.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)

Write implements io.Writer.Write.

type IOSequence

type IOSequence struct {
	IO    IO
	Addrs hostarch.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: Same as CopyInVec.

func (IOSequence) CopyInTo

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

CopyInTo invokes s.CopyInTo over s.Addrs.

Preconditions: Same as 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: Same as CopyOutVec.

func (IOSequence) CopyOutFrom

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

CopyOutFrom invokes s.CopyOutFrom over s.Addrs.

Preconditions: Same as IO.CopyOutFrom.

func (IOSequence) DropFirst

func (s IOSequence) DropFirst(n int) IOSequence

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

Preconditions: Same as hostarch.AddrRangeSeq.DropFirst.

func (IOSequence) DropFirst64

func (s IOSequence) DropFirst64(n int64) IOSequence

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

Preconditions: Same as hostarch.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, linuxerr.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

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: Same as hostarch.AddrRangeSeq.TakeFirst.

func (IOSequence) TakeFirst64

func (s IOSequence) TakeFirst64(n int64) IOSequence

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

Preconditions: Same as hostarch.AddrRangeSeq.TakeFirst64.

func (IOSequence) 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: Same as ZeroOutVec.

type IOSequenceReadWriter

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

IOSequenceReadWriter implements io.Reader and io.Writer for an IOSequence.

func (*IOSequenceReadWriter) Init

func (rw *IOSequenceReadWriter) Init(ctx context.Context, src IOSequence)

Init initializes the IOSequence.

func (*IOSequenceReadWriter) Len

func (rw *IOSequenceReadWriter) Len() int

Len implements tcpip.Payloader.

func (*IOSequenceReadWriter) Read

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

Read implements io.Reader.Read.

func (*IOSequenceReadWriter) Write

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

Write implements io.Writer.Write.

Jump to

Keyboard shortcuts

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