PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
源码索引 / CURRENT IMPLEMENTATION

cow_linux.go

201 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。

跳到函数 / 声明
//go:build linux

package spine

import (
	"errors"
	"fmt"
	"runtime"
	"sync"
	"unsafe"

	"github.com/tetratelabs/wazero/api"
	"github.com/tetratelabs/wazero/experimental"
	"golang.org/x/sys/unix"
)

const wasmPageSize = 65536

var cowPageSize = unix.Getpagesize()

type linuxCOW struct {
	fd        int
	imageSize uint64
	closed    bool
}

type linuxCOWMemory struct {
	mapping []byte
	max     int
	mu      sync.Mutex
}

func newCOWRuntime() (cowRuntime, error) {
	fd, err := unix.MemfdCreate("pysolate-spine-cow", unix.MFD_CLOEXEC|unix.MFD_ALLOW_SEALING)
	if err != nil {
		return nil, fmt.Errorf("create COW image memfd: %w", err)
	}
	return &linuxCOW{fd: fd}, nil
}

func (c *linuxCOW) allocator(def api.MemoryDefinition) (cowMemory, error) {
	if def == nil {
		return nil, errors.New("COW needs an exported memory named memory")
	}
	maximum, bounded := def.Max()
	if !bounded {
		return nil, errors.New("COW needs a declared memory maximum")
	}
	return newLinuxCOWMemory(uint64(maximum) * wasmPageSize)
}

func newLinuxCOWMemory(maximum uint64) (*linuxCOWMemory, error) {
	if maximum == 0 || maximum > uint64(maxInt()) {
		return nil, fmt.Errorf("invalid COW memory maximum: %d", maximum)
	}
	mapping, err := unix.Mmap(-1, 0, int(maximum), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_ANONYMOUS)
	if err != nil {
		return nil, fmt.Errorf("reserve COW linear memory (%d bytes): %w", maximum, err)
	}
	return &linuxCOWMemory{mapping: mapping, max: int(maximum)}, nil
}

// The one-memory Guest gets the range reserved before InstantiateModule.
func (m *linuxCOWMemory) Allocate(_, _ uint64) experimental.LinearMemory {
	return m
}

func (m *linuxCOWMemory) Reallocate(size uint64) []byte {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.mapping == nil {
		return nil
	}
	if size > uint64(m.max) || size > uint64(maxInt()) {
		return nil
	}
	return m.mapping[:int(size):m.max]
}

func (m *linuxCOWMemory) Free() {
	// Wazero's allocator hook has no error return.
	_ = m.release()
}

func (m *linuxCOWMemory) release() error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if m.mapping != nil {
		if err := unix.Munmap(m.mapping); err != nil {
			return err
		}
		m.mapping = nil
	}
	return nil
}

func (c *linuxCOW) capture(memory api.Memory) error {
	if memory == nil || memory.Size() == 0 {
		return errors.New("cannot capture empty COW memory")
	}
	data, ok := memory.Read(0, memory.Size())
	if !ok {
		return errors.New("cannot read initialized Guest memory for COW image")
	}
	return c.captureBytes(data)
}

// captureBytes writes directly from the allocator-backed mapping into the
// sealed memfd. It deliberately does not retain a Go-owned image copy.
func (c *linuxCOW) captureBytes(data []byte) error {
	if len(data) == 0 || len(data)%cowPageSize != 0 || len(data)%wasmPageSize != 0 {
		return fmt.Errorf("COW image size must be Wasm-page aligned: %d", len(data))
	}
	if c.closed {
		return errors.New("COW image is closed")
	}
	if c.imageSize != 0 {
		return errors.New("COW image already captured")
	}
	if err := unix.Ftruncate(c.fd, int64(len(data))); err != nil {
		return fmt.Errorf("size COW image: %w", err)
	}
	for offset := 0; offset < len(data); {
		n, err := unix.Pwrite(c.fd, data[offset:], int64(offset))
		if err != nil {
			return fmt.Errorf("write COW image: %w", err)
		}
		if n == 0 {
			return errors.New("short write while creating COW image")
		}
		offset += n
	}
	seals := unix.F_SEAL_SHRINK | unix.F_SEAL_GROW | unix.F_SEAL_WRITE | unix.F_SEAL_SEAL
	if _, err := unix.FcntlInt(uintptr(c.fd), unix.F_ADD_SEALS, seals); err != nil {
		return fmt.Errorf("seal COW image: %w", err)
	}
	c.imageSize = uint64(len(data))
	return nil
}

func (c *linuxCOW) ready() bool {
	return !c.closed && c.imageSize != 0
}

func (c *linuxCOW) attach(memory api.Memory) error {
	if memory == nil {
		return errors.New("cannot attach COW image to nil memory")
	}
	if uint64(memory.Size()) < c.imageSize {
		pages := uint32((c.imageSize - uint64(memory.Size())) / wasmPageSize)
		if _, ok := memory.Grow(pages); !ok {
			return errors.New("cannot grow memory to COW image")
		}
	}
	data, ok := memory.Read(0, memory.Size())
	if !ok {
		return errors.New("cannot inspect Guest memory for COW attach")
	}
	return c.attachBytes(data)
}

// attachBytes replaces exactly the current mapping range. MAP_PRIVATE keeps
// the sealed file pages shared until a Guest write faults a page private.
func (c *linuxCOW) attachBytes(data []byte) error {
	if c.closed {
		return errors.New("COW image is closed")
	}
	if c.imageSize == 0 || uint64(len(data)) != c.imageSize {
		return fmt.Errorf("COW memory shape changed: got %d, want %d", len(data), c.imageSize)
	}
	if len(data) == 0 || uintptr(unsafe.Pointer(unsafe.SliceData(data)))%uintptr(cowPageSize) != 0 {
		return errors.New("COW memory address is not page-aligned")
	}
	address := unsafe.Pointer(unsafe.SliceData(data))
	mapped, err := unix.MmapPtr(c.fd, 0, address, uintptr(len(data)), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_FIXED)
	if err != nil {
		return fmt.Errorf("map sealed COW image at existing address: %w", err)
	}
	if mapped != address {
		_ = unix.MunmapPtr(mapped, uintptr(len(data)))
		return errors.New("COW mapping address changed")
	}
	runtime.KeepAlive(data)
	return nil
}

func (c *linuxCOW) close() error {
	if c.closed {
		return nil
	}
	c.closed = true
	if err := unix.Close(c.fd); err != nil {
		return fmt.Errorf("close COW image: %w", err)
	}
	c.fd = -1
	return nil
}

func maxInt() int {
	return int(^uint(0) >> 1)
}