github.com/Rookout/GoSDK@v0.1.48/pkg/services/protector/mprotect_darwin.go (about)

     1  //go:build darwin
     2  // +build darwin
     3  
     4  package protector
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/Rookout/GoSDK/pkg/rookoutErrors"
    10  )
    11  
    12  /* #cgo CFLAGS:
    13  #include <mach/mach.h>
    14  #include <sys/mman.h>
    15  #include <libkern/OSCacheControl.h>
    16  #include <unistd.h>
    17  
    18  #ifdef __aarch64__
    19  void  __attribute__ ((noinline)) align_mprotect_darwin_start() {
    20      asm(".align 14");
    21  }
    22  #endif
    23  
    24  int get_current_memory_protection(uint64_t addr, uint64_t size) {
    25      vm_address_t vm_addr = (vm_address_t) addr;
    26      vm_size_t vm_size = (vm_size_t) size;
    27      vm_region_basic_info_data_64_t info;
    28      mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
    29      memory_object_name_t object;
    30  
    31      kern_return_t kret = vm_region_64(mach_task_self(), &vm_addr, &vm_size, VM_REGION_BASIC_INFO_64,
    32                                          (vm_region_info_t) &info, &info_count, &object);
    33  
    34  	if (kret != KERN_SUCCESS) {
    35  		return -1;
    36  	}
    37  
    38      return info.protection;
    39  }
    40  
    41  int mprotect_darwin(uint64_t addr, uint64_t size, int protection) {
    42      if (mprotect((void *)addr, (size_t)size, protection) != 0) {
    43          // When a caller finds that they cannot obtain write permission on a mapped entry, the following VM_PROT_COPY flag
    44          // can be used.
    45          // The entry will be made "needs copy" effectively copying the object (using COW), and write permission will be added
    46          // to the maximum protections for the associated entry.
    47  #ifdef __aarch64__
    48          if ((protection & PROT_EXEC) == 0) {
    49              protection = protection | VM_PROT_COPY;
    50          }
    51  #else
    52          protection = protection | VM_PROT_COPY;
    53  #endif
    54          kern_return_t kret = vm_protect(mach_task_self(), addr, size, 0,
    55                                          protection);
    56          if (kret != KERN_SUCCESS) {
    57  			return kret;
    58          }
    59      }
    60  
    61      // * https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/caches-and-self-modifying-code
    62      // * https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html
    63      // * In arm the instruction cache isn't invalidated when modifying the code. This could cause callers to see the old
    64      // * value before making any modification (or even worse, the old permissions).
    65      // * Here we use apple's functions for invalidating the cache. First we flush the dcache, and then we invalidate
    66      // * the icache. This ensures that any modifications to cache lines (such as modified data and permissions modification).
    67      // * We probably don't need to flush the dcache since invalidating the icache should be enough, but we do it
    68      // * for completeness and safety.
    69  #ifdef __aarch64__
    70      sys_dcache_flush((void *) addr, size);
    71      sys_icache_invalidate((void *) addr, size);
    72  #endif
    73  
    74  	return 0;
    75  }
    76  
    77  #ifdef __aarch64__
    78  void  __attribute__ ((noinline)) align_mprotect_darwin_end() {
    79  	asm(".align 14");
    80  }
    81  #endif
    82  */
    83  import "C"
    84  
    85  func GetMemoryProtection(addr uint64, size uint64) (int, rookoutErrors.RookoutError) {
    86  	prot := int(C.get_current_memory_protection(C.ulonglong(addr), C.ulonglong(size)))
    87  	if prot == -1 {
    88  		return 0, rookoutErrors.NewFailedToGetCurrentMemoryProtection(addr, size)
    89  	}
    90  	return prot, nil
    91  }
    92  
    93  func ChangeMemoryProtection(start uintptr, end uintptr, prot int) rookoutErrors.RookoutError {
    94  	ret := C.mprotect_darwin(C.ulonglong(start), C.ulonglong(end-start), C.int(prot))
    95  	if ret != 0 {
    96  		rookoutErrors.NewMprotectFailed(start, int(end-start), prot, fmt.Sprintf("mprotect failed: %d", ret))
    97  	}
    98  	return nil
    99  }