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 }