-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathimage_linux.go
More file actions
120 lines (112 loc) · 3.55 KB
/
Copy pathimage_linux.go
File metadata and controls
120 lines (112 loc) · 3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//go:build linux && (arm64 || amd64) && cgo
package sandbox
import (
"bufio"
"context"
"encoding/binary"
"fmt"
"io"
"math"
"github.com/xgo-dev/sandbox/internal/state"
"golang.org/x/sys/unix"
)
func newStateImage() (int, error) {
fd, err := unix.MemfdCreate("llar-sandbox", unix.MFD_CLOEXEC|unix.MFD_ALLOW_SEALING)
if err != nil {
return -1, err
}
// The guest may append its result, but cannot truncate a live mapping.
// The host adds the final write and seal locks before reading that result.
if _, err := unix.FcntlInt(uintptr(fd), unix.F_ADD_SEALS, unix.F_SEAL_SHRINK); err != nil {
unix.Close(fd)
return -1, err
}
return fd, nil
}
// Each image starts with its total byte length, including the eight-byte header.
// The input and result occupy consecutive images in the same memfd. The host
// retains the result offset independently of the guest-writable input header.
func writeStateImage(fd int, offset int64, graph *state.State, root any) (int64, error) {
if offset < 0 || offset > math.MaxInt64-16 {
return 0, fmt.Errorf("sandbox image size overflow")
}
output := imageWriter{fd: fd, offset: offset}
var header [8]byte
if _, err := output.Write(header[:]); err != nil {
return 0, err
}
buffer := bufio.NewWriter(&output)
n, _, err := graph.SaveTo(context.Background(), buffer, root)
if err != nil {
return 0, err
}
if err := buffer.Flush(); err != nil {
return 0, err
}
// An unwritten result must keep its following header unpublished.
if _, err := output.Write(header[:]); err != nil {
return 0, err
}
size := int64(n) + 8
// Publish the size only after the payload is complete. The reader must also
// wait for ownership to transfer; this header is not a synchronization lock.
binary.LittleEndian.PutUint64(header[:], uint64(size))
output.offset = offset
if _, err := output.Write(header[:]); err != nil {
return 0, err
}
return size, nil
}
// imageWriter keeps the input and result independent of the shared fd offset.
type imageWriter struct {
fd int
offset int64
}
func (w *imageWriter) Write(p []byte) (int, error) {
if w.offset < 0 || w.offset > math.MaxInt64-int64(len(p)) {
return 0, fmt.Errorf("sandbox image size overflow")
}
for {
n, err := unix.Pwrite(w.fd, p, w.offset)
if err == unix.EINTR {
continue
}
n = max(n, 0)
w.offset += int64(n)
if err == nil && n != len(p) {
err = io.ErrShortWrite
}
return n, err
}
}
// The sender must have finished before reading. The host also seals returned
// images against writes. The caller must keep the mapping until its state
// round trip is complete, then release it using the returned function.
func readStateImage(fd int, offset int64) ([]byte, func() error, error) {
var header [8]byte
n, err := unix.Pread(fd, header[:], offset)
if err != nil {
return nil, nil, err
}
if n != len(header) {
return nil, nil, io.ErrUnexpectedEOF
}
size := binary.LittleEndian.Uint64(header[:])
var stat unix.Stat_t
if err := unix.Fstat(fd, &stat); err != nil {
return nil, nil, err
}
if offset < 0 || offset > stat.Size || size < 8 || size > uint64(stat.Size-offset) {
return nil, nil, fmt.Errorf("invalid or incomplete sandbox image length %d", size)
}
mapOffset := offset - offset%int64(unix.Getpagesize())
delta := int(offset - mapOffset)
if size > uint64(math.MaxInt-delta) {
return nil, nil, fmt.Errorf("sandbox mapping size overflow")
}
mem, err := unix.Mmap(fd, mapOffset, delta+int(size), unix.PROT_READ, unix.MAP_SHARED)
if err != nil {
return nil, nil, err
}
return mem[delta+8 : delta+int(size)], func() error { return unix.Munmap(mem) }, nil
}