github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/flipcall/packet_window.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package flipcall 16 17 import ( 18 "fmt" 19 "math/bits" 20 "os" 21 22 "golang.org/x/sys/unix" 23 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 24 "github.com/nicocha30/gvisor-ligolo/pkg/memutil" 25 ) 26 27 var ( 28 pageSize = os.Getpagesize() 29 pageMask = pageSize - 1 30 ) 31 32 func init() { 33 if bits.OnesCount(uint(pageSize)) != 1 { 34 // This is depended on by roundUpToPage(). 35 panic(fmt.Sprintf("system page size (%d) is not a power of 2", pageSize)) 36 } 37 if uintptr(pageSize) < PacketHeaderBytes { 38 // This is required since Endpoint.Init() imposes a minimum packet 39 // window size of 1 page. 40 panic(fmt.Sprintf("system page size (%d) is less than packet header size (%d)", pageSize, PacketHeaderBytes)) 41 } 42 } 43 44 // PacketWindowDescriptor represents a packet window, a range of pages in a 45 // shared memory file that is used to exchange packets between partner 46 // Endpoints. 47 type PacketWindowDescriptor struct { 48 // FD is the file descriptor representing the shared memory file. 49 FD int 50 51 // Offset is the offset into the shared memory file at which the packet 52 // window begins. 53 Offset int64 54 55 // Length is the size of the packet window in bytes. 56 Length int 57 } 58 59 // PacketWindowLengthForDataCap returns the minimum packet window size required 60 // to accommodate datagrams of the given size in bytes. 61 func PacketWindowLengthForDataCap(dataCap uint32) int { 62 return roundUpToPage(int(dataCap) + int(PacketHeaderBytes)) 63 } 64 65 func roundUpToPage(x int) int { 66 return (x + pageMask) &^ pageMask 67 } 68 69 // A PacketWindowAllocator owns a shared memory file, and allocates packet 70 // windows from it. 71 type PacketWindowAllocator struct { 72 fd int 73 nextAlloc int64 74 fileSize int64 75 } 76 77 // Init must be called on zero-value PacketWindowAllocators before first use. 78 // If it succeeds, Destroy() must be called once the PacketWindowAllocator is 79 // no longer in use. 80 func (pwa *PacketWindowAllocator) Init() error { 81 fd, err := memutil.CreateMemFD("flipcall_packet_windows", linux.MFD_CLOEXEC|linux.MFD_ALLOW_SEALING) 82 if err != nil { 83 return fmt.Errorf("failed to create memfd: %v", err) 84 } 85 // Apply F_SEAL_SHRINK to prevent either party from causing SIGBUS in the 86 // other by truncating the file, and F_SEAL_SEAL to prevent either party 87 // from applying F_SEAL_GROW or F_SEAL_WRITE. 88 if _, _, e := unix.RawSyscall(unix.SYS_FCNTL, uintptr(fd), linux.F_ADD_SEALS, linux.F_SEAL_SHRINK|linux.F_SEAL_SEAL); e != 0 { 89 unix.Close(fd) 90 return fmt.Errorf("failed to apply memfd seals: %v", e) 91 } 92 pwa.fd = fd 93 return nil 94 } 95 96 // NewPacketWindowAllocator is a convenience function that returns an 97 // initialized PacketWindowAllocator allocated on the heap. 98 func NewPacketWindowAllocator() (*PacketWindowAllocator, error) { 99 var pwa PacketWindowAllocator 100 if err := pwa.Init(); err != nil { 101 return nil, err 102 } 103 return &pwa, nil 104 } 105 106 // Destroy releases resources owned by pwa. This invalidates file descriptors 107 // previously returned by pwa.FD() and pwd.Allocate(). 108 func (pwa *PacketWindowAllocator) Destroy() { 109 unix.Close(pwa.fd) 110 } 111 112 // FD represents the file descriptor of the shared memory file backing pwa. 113 func (pwa *PacketWindowAllocator) FD() int { 114 return pwa.fd 115 } 116 117 // Allocate allocates a new packet window of at least the given size and 118 // returns a PacketWindowDescriptor representing it. 119 // 120 // Preconditions: size > 0. 121 func (pwa *PacketWindowAllocator) Allocate(size int) (PacketWindowDescriptor, error) { 122 if size <= 0 { 123 return PacketWindowDescriptor{}, fmt.Errorf("invalid size: %d", size) 124 } 125 // Page-align size to ensure that pwa.nextAlloc remains page-aligned. 126 size = roundUpToPage(size) 127 if size <= 0 { 128 return PacketWindowDescriptor{}, fmt.Errorf("size %d overflows after rounding up to page size", size) 129 } 130 end := pwa.nextAlloc + int64(size) // overflow checked by ensureFileSize 131 if err := pwa.ensureFileSize(end); err != nil { 132 return PacketWindowDescriptor{}, err 133 } 134 start := pwa.nextAlloc 135 pwa.nextAlloc = end 136 return PacketWindowDescriptor{ 137 FD: pwa.FD(), 138 Offset: start, 139 Length: size, 140 }, nil 141 } 142 143 func (pwa *PacketWindowAllocator) ensureFileSize(min int64) error { 144 if min <= 0 { 145 return fmt.Errorf("file size would overflow") 146 } 147 if pwa.fileSize >= min { 148 return nil 149 } 150 newSize := 2 * pwa.fileSize 151 if newSize == 0 { 152 newSize = int64(pageSize) 153 } 154 for newSize < min { 155 newNewSize := newSize * 2 156 if newNewSize <= 0 { 157 return fmt.Errorf("file size would overflow") 158 } 159 newSize = newNewSize 160 } 161 if err := unix.Ftruncate(pwa.FD(), newSize); err != nil { 162 return fmt.Errorf("ftruncate failed: %v", err) 163 } 164 pwa.fileSize = newSize 165 return nil 166 }