-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmemory.go
More file actions
175 lines (153 loc) · 4.84 KB
/
Copy pathmemory.go
File metadata and controls
175 lines (153 loc) · 4.84 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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//go:build darwin && arm64
package hypervisor
/*
#include <Hypervisor/hv.h>
#include <Hypervisor/hv_error.h>
#ifndef HV_MEMORY_READ
#define HV_MEMORY_READ (1<<0)
#endif
#ifndef HV_MEMORY_WRITE
#define HV_MEMORY_WRITE (1<<1)
#endif
#ifndef HV_MEMORY_EXEC
#define HV_MEMORY_EXEC (1<<2)
#endif
extern int hv_vm_map(void* uva, unsigned long long gpa, size_t size, int flags);
extern int hv_vm_unmap(unsigned long long gpa, size_t size);
// Wrapper to construct flags using framework macros without exposing values to Go.
static int go_hv_vm_map(void* addr, unsigned long long gpa, unsigned long long size, int r, int w, int x) {
int flags = 0;
if (r) flags |= HV_MEMORY_READ;
if (w) flags |= HV_MEMORY_WRITE;
if (x) flags |= HV_MEMORY_EXEC;
return hv_vm_map(addr, gpa, (size_t)size, flags);
}
static int go_hv_vm_unmap(unsigned long long gpa, unsigned long long size) {
return hv_vm_unmap(gpa, (size_t)size);
}
*/
import "C"
import (
"fmt"
"math"
"runtime"
"sync"
"unsafe"
"golang.org/x/sys/unix"
)
var (
cachedPageSize int
cachedPageMask uint64 // For fast alignment checks: addr & mask == 0
pageSizeOnce sync.Once
)
// pageSize returns the system page size, cached for performance
func pageSize() int {
pageSizeOnce.Do(func() {
cachedPageSize = unix.Getpagesize()
cachedPageMask = uint64(cachedPageSize - 1)
})
return cachedPageSize
}
// isPageAligned returns true if addr is page-aligned (fast path)
func isPageAligned(addr uint64) bool {
pageSizeOnce.Do(func() {
cachedPageSize = unix.Getpagesize()
cachedPageMask = uint64(cachedPageSize - 1)
})
return addr&cachedPageMask == 0
}
// Map maps a host memory slice into the guest physical address space.
// The host slice base address, length, and guestPhys must be page-aligned.
func (vm *VM) Map(host []byte, guestPhys uint64, perms MemPerm) error {
if vm == nil {
return fmt.Errorf("hv: VM is nil")
}
if vm.closed {
return fmt.Errorf("hv: VM is closed")
}
if len(host) == 0 {
return fmt.Errorf("hv: map requires non-empty host buffer")
}
// Security: Prevent integer overflow vulnerabilities
if len(host) > math.MaxInt32 {
return fmt.Errorf("hv: host buffer too large (max %d bytes)", math.MaxInt32)
}
if guestPhys > math.MaxUint64-uint64(len(host)) {
return fmt.Errorf("hv: guest address range would overflow")
}
// Validate permissions - must have at least one permission set
if perms == 0 {
return fmt.Errorf("hv: map requires at least one permission (read, write, or exec)")
}
// Check for invalid permission bits
validPerms := MemRead | MemWrite | MemExec
if perms&^validPerms != 0 {
return fmt.Errorf("hv: invalid permission bits 0x%x (valid: 0x%x)", perms, validPerms)
}
// Performance: Fast alignment checks using cached masks
if !isPageAligned(guestPhys) {
return fmt.Errorf("hv: guestPhys not page-aligned: 0x%x (page size: %d)", guestPhys, pageSize())
}
if !isPageAligned(uint64(len(host))) {
return fmt.Errorf("hv: host length not page multiple: %d (page size: %d)", len(host), pageSize())
}
// Pin the memory before passing to C to prevent GC from moving it
runtime.KeepAlive(host)
defer runtime.KeepAlive(host)
ptr := unsafe.Pointer(&host[0])
if !isPageAligned(uint64(uintptr(ptr))) {
return fmt.Errorf("hv: host base not page-aligned: %p (page size: %d)", ptr, pageSize())
}
read := 0
write := 0
exec := 0
if perms&MemRead != 0 {
read = 1
}
if perms&MemWrite != 0 {
write = 1
}
if perms&MemExec != 0 {
exec = 1
}
ret := C.go_hv_vm_map(ptr, C.ulonglong(guestPhys), C.ulonglong(uint64(len(host))), C.int(read), C.int(write), C.int(exec))
if err := hvErr(ret); err != nil {
recordResourceError()
return fmt.Errorf("failed to map %d bytes at 0x%x with perms 0x%x: %w", len(host), guestPhys, perms, err)
}
recordMapOperation()
return nil
}
// Unmap removes a region from the guest physical address space.
func (vm *VM) Unmap(guestPhys, size uint64) error {
if vm == nil {
return fmt.Errorf("hv: VM is nil")
}
if vm.closed {
return fmt.Errorf("hv: VM is closed")
}
if size == 0 {
return fmt.Errorf("hv: unmap requires non-zero size")
}
// Security: Prevent integer overflow vulnerabilities
if size > math.MaxInt32 {
return fmt.Errorf("hv: unmap size too large (max %d bytes)", math.MaxInt32)
}
if guestPhys > math.MaxUint64-size {
return fmt.Errorf("hv: guest address range would overflow")
}
// Performance: Fast alignment checks using cached masks
if !isPageAligned(guestPhys) {
return fmt.Errorf("hv: guestPhys not page-aligned: 0x%x (page size: %d)", guestPhys, pageSize())
}
if !isPageAligned(size) {
return fmt.Errorf("hv: size not page multiple: %d (page size: %d)", size, pageSize())
}
ret := C.go_hv_vm_unmap(C.ulonglong(guestPhys), C.ulonglong(size))
if err := hvErr(ret); err != nil {
recordResourceError()
return fmt.Errorf("failed to unmap region 0x%x+%d: %w", guestPhys, size, err)
}
recordUnmapOperation()
return nil
}