github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/platform/ptrace/stub_unsafe.go (about) 1 // Copyright 2018 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 ptrace 16 17 import ( 18 "reflect" 19 "unsafe" 20 21 "golang.org/x/sys/unix" 22 "github.com/metacubex/gvisor/pkg/hostarch" 23 "github.com/metacubex/gvisor/pkg/safecopy" 24 ) 25 26 // stub is defined in arch-specific assembly. 27 func stub() 28 29 // addrOfStub returns the start address of stub. 30 // 31 // In Go 1.17+, Go references to assembly functions resolve to an ABIInternal 32 // wrapper function rather than the function itself. We must reference from 33 // assembly to get the ABI0 (i.e., primary) address. 34 func addrOfStub() uintptr 35 36 // stubCall calls the stub at the given address with the given pid. 37 func stubCall(addr, pid uintptr) 38 39 // unsafeSlice returns a slice for the given address and length. 40 func unsafeSlice(addr uintptr, length int) (slice []byte) { 41 sh := (*reflect.SliceHeader)(unsafe.Pointer(&slice)) 42 sh.Data = addr 43 sh.Len = length 44 sh.Cap = length 45 return 46 } 47 48 // stubInit initializes the stub. 49 func stubInit() { 50 // Grab the existing stub. 51 stubBegin := addrOfStub() 52 stubLen := int(safecopy.FindEndAddress(stubBegin) - stubBegin) 53 stubSlice := unsafeSlice(stubBegin, stubLen) 54 mapLen := uintptr(stubLen) 55 if offset := mapLen % hostarch.PageSize; offset != 0 { 56 mapLen += hostarch.PageSize - offset 57 } 58 59 for stubStart > 0 { 60 // Map the target address for the stub. 61 // 62 // We don't use FIXED here because we don't want to unmap 63 // something that may have been there already. We just walk 64 // down the address space until we find a place where the stub 65 // can be placed. 66 addr, _, errno := unix.RawSyscall6( 67 unix.SYS_MMAP, 68 stubStart, 69 mapLen, 70 unix.PROT_WRITE|unix.PROT_READ, 71 unix.MAP_PRIVATE|unix.MAP_ANONYMOUS, 72 0 /* fd */, 0 /* offset */) 73 if addr != stubStart || errno != 0 { 74 if addr != 0 { 75 // Unmap the region we've mapped accidentally. 76 unix.RawSyscall(unix.SYS_MUNMAP, addr, mapLen, 0) 77 } 78 79 // Attempt to begin at a lower address. 80 stubStart -= uintptr(hostarch.PageSize) 81 continue 82 } 83 84 // Copy the stub to the address. 85 targetSlice := unsafeSlice(addr, stubLen) 86 copy(targetSlice, stubSlice) 87 88 // Make the stub executable. 89 if _, _, errno := unix.RawSyscall( 90 unix.SYS_MPROTECT, 91 stubStart, 92 mapLen, 93 unix.PROT_EXEC|unix.PROT_READ); errno != 0 { 94 panic("mprotect failed: " + errno.Error()) 95 } 96 97 // Set the end. 98 stubEnd = stubStart + mapLen 99 return 100 } 101 102 // This will happen only if we exhaust the entire address 103 // space, and it will take a long, long time. 104 panic("failed to map stub") 105 }