Skip to content

Instantly share code, notes, and snippets.

@vimt
Last active May 29, 2023 03:39
Show Gist options
  • Save vimt/a165366ec833db84c7c5e309d342fb7b to your computer and use it in GitHub Desktop.
Save vimt/a165366ec833db84c7c5e309d342fb7b to your computer and use it in GitHub Desktop.
Mac M1 mprotect test (for gomonkey)

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:

  1. m1 memory page can't has W+X at the same time
  2. When create memory page by mmap with the MAP_JIT parameter, then you can mprotect to W+X
  3. 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😭

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment