github.com/undefinedlabs/go-mpatch@v1.0.8-0.20230904093002-fbac8a0d7853/patcher_unix.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package mpatch
     5  
     6  import (
     7  	"reflect"
     8  	"syscall"
     9  	"time"
    10  	"unsafe"
    11  )
    12  
    13  var writeAccess = syscall.PROT_READ | syscall.PROT_WRITE | syscall.PROT_EXEC
    14  var readAccess = syscall.PROT_READ | syscall.PROT_EXEC
    15  
    16  //go:nosplit
    17  func getMemorySliceFromUintptr(p uintptr, length int) []byte {
    18  	return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
    19  		Data: p,
    20  		Len:  length,
    21  		Cap:  length,
    22  	}))
    23  }
    24  
    25  //go:nosplit
    26  func callMProtect(addr unsafe.Pointer, length int, prot int) error {
    27  	for p := uintptr(addr) & ^(uintptr(pageSize - 1)); p < uintptr(addr)+uintptr(length); p += uintptr(pageSize) {
    28  		page := getMemorySliceFromUintptr(p, pageSize)
    29  		if err := syscall.Mprotect(page, prot); err != nil {
    30  			return err
    31  		}
    32  	}
    33  	return nil
    34  }
    35  
    36  func writeDataToPointer(ptr unsafe.Pointer, data []byte) error {
    37  	dataLength := len(data)
    38  	ptrByteSlice := getMemorySliceFromPointer(ptr, len(data))
    39  	if err := callMProtect(ptr, dataLength, writeAccess); err != nil {
    40  		return err
    41  	}
    42  	copy(ptrByteSlice, data[:])
    43  	if err := callMProtect(ptr, dataLength, readAccess); err != nil {
    44  		return err
    45  	}
    46  	<-time.After(100 * time.Microsecond) // If we remove this line then it fails to unpatch on ARM64
    47  	return nil
    48  }