do some test about m1 can't use gomonkey (issue: macOS permission denied)
use this code to test
package main
import (
"fmt"
"reflect"
"syscall"
"unsafe"
)
func protBits2Str(prot int) string {
result := ""
if prot&syscall.PROT_READ != 0 {
result += "R"
} else {
result += "-"
}
if prot&syscall.PROT_WRITE != 0 {
result += "W"
} else {
result += "-"
}
if prot&syscall.PROT_EXEC != 0 {
result += "X"
} else {
result += "-"
}
return result
}
func mmap(size int, prot int, needJit bool) ([]byte, error) {
mmapFlags := syscall.MAP_ANON | syscall.MAP_PRIVATE
if needJit {
mmapFlags |= syscall.MAP_JIT
}
return syscall.Mmap(-1, 0, size, prot, mmapFlags)
}
func mprotect(b []byte, prot int) error {
return syscall.Mprotect(b, prot)
}
func testMmapAndMprotect(withJit bool) {
for mmapProt := 0; mmapProt < 8; mmapProt++ {
for mprotectProt := 0; mprotectProt < 8; mprotectProt++ {
var data []byte
var mmapErr error
var mprotectErr error
data, mmapErr = mmap(4096, mmapProt, withJit)
if mmapErr == nil {
mprotectErr = mprotect(data, mprotectProt)
}
str := "Try mmap"
if withJit {
str += fmt.Sprintf(" with MAP_JIT")
}
str += ":"
str += protBits2Str(mmapProt)
if mmapErr != nil {
str += " FAIL:"
str += mmapErr.Error()
} else {
str += fmt.Sprintf(" PASS:%p", data)
}
if mmapErr == nil {
str += " "
str += "Try mprotect:"
str += protBits2Str(mprotectProt)
if mprotectErr != nil {
str += " FAIL:"
str += mprotectErr.Error()
}
}
fmt.Println(str)
}
}
}
//go:noinline
func add(a int, b int) int {
return a + b
}
func pageStart(ptr uintptr) uintptr {
return ptr & ^(uintptr(syscall.Getpagesize() - 1))
}
func entryAddress(p uintptr, l int) []byte {
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: p, Len: l, Cap: l}))
}
type funcValue struct {
_ uintptr
p unsafe.Pointer
}
func getPointer(v reflect.Value) unsafe.Pointer {
return (*funcValue)(unsafe.Pointer(&v)).p
}
func testTextSectionMprotect() {
target := reflect.ValueOf(add)
funcAddress := *(*uintptr)(getPointer(target))
pagesize := syscall.Getpagesize()
funcPageStart := pageStart(funcAddress)
page := entryAddress(funcPageStart, pagesize)
tryProt := func(prot int) {
err := syscall.Mprotect(page, prot)
str := fmt.Sprintf("mprotect func add page: %x (real func: %x) prot: %s", funcPageStart, funcAddress, protBits2Str(prot))
if err != nil {
str += " FAIL:"
str += err.Error()
}
fmt.Println(str)
}
// MUST have X
tryProt(4)
tryProt(5)
tryProt(6)
tryProt(7)
}
func main() {
testMmapAndMprotect(false)
testMmapAndMprotect(true)
testTextSectionMprotect()
}
and output
Try mmap:--- PASS:0x10a0a8000 Try mprotect:---
Try mmap:--- PASS:0x10a0ac000 Try mprotect:R--
Try mmap:--- PASS:0x10a0b0000 Try mprotect:-W-
Try mmap:--- PASS:0x10a0b4000 Try mprotect:RW-
Try mmap:--- PASS:0x10a0b8000 Try mprotect:--X
Try mmap:--- PASS:0x10a0bc000 Try mprotect:R-X
Try mmap:--- PASS:0x10a0c0000 Try mprotect:-WX FAIL:permission denied
Try mmap:--- PASS:0x10a0c4000 Try mprotect:RWX FAIL:permission denied
Try mmap:R-- PASS:0x10a0c8000 Try mprotect:---
Try mmap:R-- PASS:0x10a0cc000 Try mprotect:R--
Try mmap:R-- PASS:0x10a0d0000 Try mprotect:-W-
Try mmap:R-- PASS:0x10a0d4000 Try mprotect:RW-
Try mmap:R-- PASS:0x10a0d8000 Try mprotect:--X
Try mmap:R-- PASS:0x10a0dc000 Try mprotect:R-X
Try mmap:R-- PASS:0x10a0e0000 Try mprotect:-WX FAIL:permission denied
Try mmap:R-- PASS:0x10a0e4000 Try mprotect:RWX FAIL:permission denied
Try mmap:-W- PASS:0x10a0e8000 Try mprotect:---
Try mmap:-W- PASS:0x10a0ec000 Try mprotect:R--
Try mmap:-W- PASS:0x10a0f0000 Try mprotect:-W-
Try mmap:-W- PASS:0x10a0f4000 Try mprotect:RW-
Try mmap:-W- PASS:0x10a0f8000 Try mprotect:--X
Try mmap:-W- PASS:0x10a0fc000 Try mprotect:R-X
Try mmap:-W- PASS:0x10a100000 Try mprotect:-WX FAIL:permission denied
Try mmap:-W- PASS:0x10a104000 Try mprotect:RWX FAIL:permission denied
Try mmap:RW- PASS:0x10a108000 Try mprotect:---
Try mmap:RW- PASS:0x10a10c000 Try mprotect:R--
Try mmap:RW- PASS:0x10a110000 Try mprotect:-W-
Try mmap:RW- PASS:0x10a114000 Try mprotect:RW-
Try mmap:RW- PASS:0x10a118000 Try mprotect:--X
Try mmap:RW- PASS:0x10a11c000 Try mprotect:R-X
Try mmap:RW- PASS:0x10a120000 Try mprotect:-WX FAIL:permission denied
Try mmap:RW- PASS:0x10a124000 Try mprotect:RWX FAIL:permission denied
Try mmap:--X PASS:0x10a128000 Try mprotect:---
Try mmap:--X PASS:0x10a12c000 Try mprotect:R--
Try mmap:--X PASS:0x10a130000 Try mprotect:-W-
Try mmap:--X PASS:0x10a134000 Try mprotect:RW-
Try mmap:--X PASS:0x10a138000 Try mprotect:--X
Try mmap:--X PASS:0x10a13c000 Try mprotect:R-X
Try mmap:--X PASS:0x10a140000 Try mprotect:-WX FAIL:permission denied
Try mmap:--X PASS:0x10a144000 Try mprotect:RWX FAIL:permission denied
Try mmap:R-X PASS:0x10a148000 Try mprotect:---
Try mmap:R-X PASS:0x10a14c000 Try mprotect:R--
Try mmap:R-X PASS:0x10a150000 Try mprotect:-W-
Try mmap:R-X PASS:0x10a154000 Try mprotect:RW-
Try mmap:R-X PASS:0x10a158000 Try mprotect:--X
Try mmap:R-X PASS:0x10a15c000 Try mprotect:R-X
Try mmap:R-X PASS:0x10a160000 Try mprotect:-WX FAIL:permission denied
Try mmap:R-X PASS:0x10a164000 Try mprotect:RWX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:-WX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap:RWX FAIL:permission denied
Try mmap with MAP_JIT:--- PASS:0x10a168000 Try mprotect:---
Try mmap with MAP_JIT:--- PASS:0x10a16c000 Try mprotect:R--
Try mmap with MAP_JIT:--- PASS:0x10a170000 Try mprotect:-W-
Try mmap with MAP_JIT:--- PASS:0x10a174000 Try mprotect:RW-
Try mmap with MAP_JIT:--- PASS:0x10a178000 Try mprotect:--X
Try mmap with MAP_JIT:--- PASS:0x10a17c000 Try mprotect:R-X
Try mmap with MAP_JIT:--- PASS:0x10a180000 Try mprotect:-WX
Try mmap with MAP_JIT:--- PASS:0x10a184000 Try mprotect:RWX
Try mmap with MAP_JIT:R-- PASS:0x10a188000 Try mprotect:---
Try mmap with MAP_JIT:R-- PASS:0x10a18c000 Try mprotect:R--
Try mmap with MAP_JIT:R-- PASS:0x10a190000 Try mprotect:-W-
Try mmap with MAP_JIT:R-- PASS:0x10a194000 Try mprotect:RW-
Try mmap with MAP_JIT:R-- PASS:0x10a198000 Try mprotect:--X
Try mmap with MAP_JIT:R-- PASS:0x10a19c000 Try mprotect:R-X
Try mmap with MAP_JIT:R-- PASS:0x10a1a0000 Try mprotect:-WX
Try mmap with MAP_JIT:R-- PASS:0x10a1a4000 Try mprotect:RWX
Try mmap with MAP_JIT:-W- PASS:0x10a1a8000 Try mprotect:---
Try mmap with MAP_JIT:-W- PASS:0x10a1ac000 Try mprotect:R--
Try mmap with MAP_JIT:-W- PASS:0x10a1b0000 Try mprotect:-W-
Try mmap with MAP_JIT:-W- PASS:0x10a1b4000 Try mprotect:RW-
Try mmap with MAP_JIT:-W- PASS:0x10a1b8000 Try mprotect:--X
Try mmap with MAP_JIT:-W- PASS:0x10a1bc000 Try mprotect:R-X
Try mmap with MAP_JIT:-W- PASS:0x10a1c0000 Try mprotect:-WX
Try mmap with MAP_JIT:-W- PASS:0x10a1c4000 Try mprotect:RWX
Try mmap with MAP_JIT:RW- PASS:0x10a1c8000 Try mprotect:---
Try mmap with MAP_JIT:RW- PASS:0x10a1cc000 Try mprotect:R--
Try mmap with MAP_JIT:RW- PASS:0x10a1d0000 Try mprotect:-W-
Try mmap with MAP_JIT:RW- PASS:0x10a1d4000 Try mprotect:RW-
Try mmap with MAP_JIT:RW- PASS:0x10a1d8000 Try mprotect:--X
Try mmap with MAP_JIT:RW- PASS:0x10a1dc000 Try mprotect:R-X
Try mmap with MAP_JIT:RW- PASS:0x10a1e0000 Try mprotect:-WX
Try mmap with MAP_JIT:RW- PASS:0x10a1e4000 Try mprotect:RWX
Try mmap with MAP_JIT:--X PASS:0x10a1e8000 Try mprotect:---
Try mmap with MAP_JIT:--X PASS:0x10a1ec000 Try mprotect:R--
Try mmap with MAP_JIT:--X PASS:0x10a1f0000 Try mprotect:-W-
Try mmap with MAP_JIT:--X PASS:0x10a1f4000 Try mprotect:RW-
Try mmap with MAP_JIT:--X PASS:0x10a1f8000 Try mprotect:--X
Try mmap with MAP_JIT:--X PASS:0x10a1fc000 Try mprotect:R-X
Try mmap with MAP_JIT:--X PASS:0x10a200000 Try mprotect:-WX
Try mmap with MAP_JIT:--X PASS:0x10a204000 Try mprotect:RWX
Try mmap with MAP_JIT:R-X PASS:0x10a208000 Try mprotect:---
Try mmap with MAP_JIT:R-X PASS:0x10a20c000 Try mprotect:R--
Try mmap with MAP_JIT:R-X PASS:0x10a210000 Try mprotect:-W-
Try mmap with MAP_JIT:R-X PASS:0x10a214000 Try mprotect:RW-
Try mmap with MAP_JIT:R-X PASS:0x10a218000 Try mprotect:--X
Try mmap with MAP_JIT:R-X PASS:0x10a21c000 Try mprotect:R-X
Try mmap with MAP_JIT:R-X PASS:0x10a220000 Try mprotect:-WX
Try mmap with MAP_JIT:R-X PASS:0x10a224000 Try mprotect:RWX
Try mmap with MAP_JIT:-WX PASS:0x10a228000 Try mprotect:--- FAIL:permission denied
Try mmap with MAP_JIT:-WX PASS:0x10a22c000 Try mprotect:R-- FAIL:permission denied
Try mmap with MAP_JIT:-WX PASS:0x10a230000 Try mprotect:-W- FAIL:permission denied
Try mmap with MAP_JIT:-WX PASS:0x10a234000 Try mprotect:RW- FAIL:permission denied
Try mmap with MAP_JIT:-WX PASS:0x10a238000 Try mprotect:--X FAIL:permission denied
Try mmap with MAP_JIT:-WX PASS:0x10a23c000 Try mprotect:R-X FAIL:permission denied
Try mmap with MAP_JIT:-WX PASS:0x10a240000 Try mprotect:-WX FAIL:permission denied
Try mmap with MAP_JIT:-WX PASS:0x10a244000 Try mprotect:RWX FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a248000 Try mprotect:--- FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a24c000 Try mprotect:R-- FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a250000 Try mprotect:-W- FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a254000 Try mprotect:RW- FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a258000 Try mprotect:--X FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a25c000 Try mprotect:R-X FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a260000 Try mprotect:-WX FAIL:permission denied
Try mmap with MAP_JIT:RWX PASS:0x10a264000 Try mprotect:RWX FAIL:permission denied
mprotect func add page: 1029f8000 (real func: 1029fbfd0) prot: --X
mprotect func add page: 1029f8000 (real func: 1029fbfd0) prot: R-X
mprotect func add page: 1029f8000 (real func: 1029fbfd0) prot: -WX FAIL:permission denied
mprotect func add page: 1029f8000 (real func: 1029fbfd0) prot: RWX FAIL:permission denied
We came to the following conclusions:
- m1 memory page can't has W+X at the same time
- When create memory page by mmap with the MAP_JIT parameter, then you can mprotect to W+X
- The TEXT page has X by default and cannot be removed, so
the TEXT segment cannot be written
Finally, I give up, and I use GOARCH=amd64
😭