gomonkey icon indicating copy to clipboard operation
gomonkey copied to clipboard

macOS permission denied

Open liangjingkanji opened this issue 3 years ago • 62 comments

Line 19: - permission denied goroutine 4 [running]:

发生在

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}

环境

  • MacOS 11.6.1 (ARM64 M1 以及 X86都如此)
  • 运行GoMonkey的最新Demo
  • 使用的gomonkey 2.2.0

描述:

  • 使用GOARCH=amd64可以解决, 但是无法debug很不幸.
  • 在Linux/Windows都是正常
  • macos-golink-wrapper 方案可以解决x86上此问题, 但是arm64依然不行
  • 如果使用单元测试框架结果导致无法断点调试也是太影响了

liangjingkanji avatar Nov 13 '21 08:11 liangjingkanji

is it fixed? It was the some in my MacOS 12.0 monterey .

panic: permission denied [recovered] panic: permission denied

goroutine 34 [running]: testing.tRunner.func1.2({0x100ec4c00, 0x101187e88}) /Users/chengfeng.han/Apps/go/sdk/go1.17.3/src/testing/testing.go:1209 +0x258 testing.tRunner.func1(0x140004829c0) /Users/chengfeng.han/Apps/go/sdk/go1.17.3/src/testing/testing.go:1212 +0x284 panic({0x100ec4c00, 0x101187e88}) /Users/chengfeng.han/Apps/go/sdk/go1.17.3/src/runtime/panic.go:1038 +0x21c github.com/agiledragon/gomonkey/v2.modifyBinary(0x100c77320, {0x140004ac090, 0x18, 0x18}) /Users/chengfeng.han/Work/工作事业/产品管理/葫芦小智/开发/源代码/hulu/hulu-edge-os/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go:11 +0x134

It's very crazy , because i was use zeromq as my library, if I use amd64 to run , it's not able to use the zeromq as it was installed as an arm version....

Anyone can help ?

sunblack110 avatar Dec 03 '21 09:12 sunblack110

Suggest to give up

liangjingkanji avatar Dec 03 '21 09:12 liangjingkanji

https://github.com/eisenxp/macos-golink-wrapper

BardVolta avatar Dec 22 '21 10:12 BardVolta

我也遇到这个问题了

xnnyeYp avatar Jan 05 '22 08:01 xnnyeYp

it is fixed by a very easy way.

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)

change to

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE)

in the function `func modifyBinary(target uintptr, bytes []byte) { function := entryAddress(target, len(bytes))

page := entryAddress(pageStart(target), syscall.Getpagesize())
err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}
copy(function, bytes)

err = syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}

}`

"github.com/agiledragon/gomonkey/v2" modify_binary_darwin.go

sunblack110 avatar Mar 15 '22 15:03 sunblack110

copy(function, bytes)

err = syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC) if err != nil { panic(err) }


}`

"github.com/agiledragon/gomonkey/v2" modify_binary_darwin.go

Please others who has the same problem test it using this method.

agiledragon avatar Mar 15 '22 22:03 agiledragon

it is fixed by a very easy way.

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)

change to

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE)

in the function `func modifyBinary(target uintptr, bytes []byte) { function := entryAddress(target, len(bytes))

page := entryAddress(pageStart(target), syscall.Getpagesize())
err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}
copy(function, bytes)

err = syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}

}`

"github.com/agiledragon/gomonkey/v2" modify_binary_darwin.go

still got "permission denied"

image

Zeahow avatar Mar 17 '22 09:03 Zeahow

I don't why it's working for me ....

sunblack110 avatar Mar 17 '22 13:03 sunblack110

I tried this and it...sort of worked? However, there seemed to be some sort of timing issue - I noticed that the patch reset wasn't taking effect.

Adding a small sleep seemed to fix that, but without understanding the problem, I don't trust that as a solution. For reference, here was my full modifyBinary() from modify_binary_darwin.go:

func modifyBinary(target uintptr, bytes []byte) {
	function := entryAddress(target, len(bytes))
	err := mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_WRITE)
	if err != nil {
		panic(err)
	}
	copy(function, bytes)
	err = mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_EXEC)
	if err != nil {
		panic(err)
	}
	time.Sleep(time.Millisecond)
}

After this change, the tests in gomonkey/test suite failed in similar ways between my M1 and non-M1 machines.

More specifically, everything passed except apply_private_method_test.go. On M1, I get an unexpected fault address, whereas on non-M1 I get retrieve method by name failed.

jonathanrhodes avatar Mar 17 '22 22:03 jonathanrhodes

arm64 MacOS can use amd64 version go, I change to go1.16.11 darwin/amd64,and https://github.com/eisenxp/macos-golink-wrapper

butcoder avatar Apr 01 '22 09:04 butcoder

Good job!

agiledragon avatar Apr 03 '22 12:04 agiledragon

arm64 MacOS can use amd64 version go, I change to go1.16.11 darwin/amd64,and https://github.com/eisenxp/macos-golink-wrapper

This will make it impossible to debug with breakpoints

liangjingkanji avatar Apr 04 '22 07:04 liangjingkanji

请问这个问题解决了吗?只能换 amd64 版本的 go 是吗

Tifinity avatar Apr 26 '22 07:04 Tifinity

换armd64无法断点调试, 而且编译性能差, 基本上等于没解决

liangjingkanji avatar Apr 26 '22 07:04 liangjingkanji

好吧 ·~·

Tifinity avatar Apr 26 '22 08:04 Tifinity

we have implemented a compile-time code rewrite to support any function mock, in general it does not rely on any architecture hack, anyone interested ? we're using it to bootstrap our test cases, wish it could finally be a solution to function-level mock problems.

xhd2015 avatar Jul 11 '22 02:07 xhd2015

@xhd2015 Show your code.

agiledragon avatar Jul 11 '22 03:07 agiledragon

@xhd2015 Show your code.

@agiledragon Hi guy I made it here: https://github.com/xhd2015/go-mock, the brief idea is take from https://go.dev/blog/cover, that we insert a Trap call in beginning of each function's body, to catch the control flow of the original function, meanwhile does not change the line layout of the original files so that debugging info are still accurate.

xhd2015 avatar Jul 11 '22 15:07 xhd2015

I tried this and it...sort of worked? However, there seemed to be some sort of timing issue - I noticed that the patch reset wasn't taking effect.

Adding a small sleep seemed to fix that, but without understanding the problem, I don't trust that as a solution. For reference, here was my full modifyBinary() from modify_binary_darwin.go:

func modifyBinary(target uintptr, bytes []byte) {
	function := entryAddress(target, len(bytes))
	err := mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_WRITE)
	if err != nil {
		panic(err)
	}
	copy(function, bytes)
	err = mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_EXEC)
	if err != nil {
		panic(err)
	}
	time.Sleep(time.Millisecond)
}

After this change, the tests in gomonkey/test suite failed in similar ways between my M1 and non-M1 machines.

More specifically, everything passed except apply_private_method_test.go. On M1, I get an unexpected fault address, whereas on non-M1 I get retrieve method by name failed.

I am using the original bouke monkey(which has the very much same code here). Still looking for a solution.

LeslieLeung avatar Aug 16 '22 11:08 LeslieLeung

Waiting for a solution

zzpwestlife avatar Sep 21 '22 02:09 zzpwestlife

Any possibility we get a solution? @agiledragon

cahan1013 avatar Oct 17 '22 09:10 cahan1013

Also, any solution for this question? @agiledragon

upidea avatar Oct 18 '22 06:10 upidea

it is fixed by a very easy way.

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)

change to

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE)

in the function `func modifyBinary(target uintptr, bytes []byte) { function := entryAddress(target, len(bytes))

page := entryAddress(pageStart(target), syscall.Getpagesize())
err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}
copy(function, bytes)

err = syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}

}`

"github.com/agiledragon/gomonkey/v2" modify_binary_darwin.go

亲测有效,在别的地方看到有说因为安全原因无法同时写和执行,把代码复制出来试了一下确实成功了。

719010539 avatar Nov 01 '22 07:11 719010539

解决了请踢我一下

Nomango avatar Nov 20 '22 12:11 Nomango

收到!

719010539 avatar Nov 20 '22 12:11 719010539

on macos, you can try command [go test -ldflags="-extldflags="-Wl,-segprot,__TEXT,rwx,rx""],go test command with option extldflags. which have same effect with https://github.com/eisenxp/macos-golink-wrapper, but do not need wrap go link

lingxialingdu avatar Nov 24 '22 06:11 lingxialingdu

I do some test of mprotect on m1.

conclusion: m1 memory page can't has W+X at same time, and TEXT segment must have X, so we can't write TEXT segment😭

GOARCH=amd64 is the only way

vimt avatar Mar 08 '23 08:03 vimt

收到!

719010539 avatar Mar 08 '23 08:03 719010539

这个问题现在解决了么

zhangtao-yucekj avatar Mar 14 '23 09:03 zhangtao-yucekj

it is fixed by a very easy way.

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)

change to

err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE)

in the function `func modifyBinary(target uintptr, bytes []byte) { function := entryAddress(target, len(bytes))

page := entryAddress(pageStart(target), syscall.Getpagesize())
err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}
copy(function, bytes)

err = syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC)
if err != nil {
    panic(err)
}

}`

"github.com/agiledragon/gomonkey/v2" modify_binary_darwin.go

thank you so much mate

jinshanwang avatar Mar 16 '23 10:03 jinshanwang