agiledragon / gomonkey

gomonkey is a library to make monkey patching in unit tests easy
MIT License
1.93k stars 178 forks source link

macOS permission denied #70

Open liangjingkanji opened 2 years ago

liangjingkanji commented 2 years ago

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)
}

环境

描述:

sunblack110 commented 2 years ago

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 ?

liangjingkanji commented 2 years ago

Suggest to give up

BardVolta commented 2 years ago

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

xnnyeYp commented 2 years ago

我也遇到这个问题了

sunblack110 commented 2 years ago

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

agiledragon commented 2 years ago

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.

Zeahow commented 2 years ago

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

sunblack110 commented 2 years ago

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

jonathanrhodes commented 2 years ago

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.

butcoder commented 2 years ago

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

agiledragon commented 2 years ago

Good job!

liangjingkanji commented 2 years ago

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

Tifinity commented 2 years ago

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

liangjingkanji commented 2 years ago

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

Tifinity commented 2 years ago

好吧 ·~·

xhd2015 commented 1 year ago

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.

agiledragon commented 1 year ago

@xhd2015 Show your code.

xhd2015 commented 1 year ago

@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.

LeslieLeung commented 1 year ago

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.

zzpwestlife commented 1 year ago

Waiting for a solution

cahan1013 commented 1 year ago

Any possibility we get a solution? @agiledragon

upidea commented 1 year ago

Also, any solution for this question? @agiledragon

719010539 commented 1 year ago

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

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

Nomango commented 1 year ago

解决了请踢我一下

719010539 commented 1 year ago

收到!

lingxialingdu commented 1 year ago

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

vimt commented 1 year ago

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

719010539 commented 1 year ago

收到!

zhangtao-yucekj commented 1 year ago

这个问题现在解决了么

jinshanwang commented 1 year ago

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

benyingY commented 1 year ago

got same issue apple M1

benyingY commented 1 year ago

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

not work for me

green4984 commented 1 year ago

Test ok at Mac M1 13.2.1 Please follow step below to solve problem "permission denied" follow this issues steam solution and test at Mac M1.


  1. find this file modify_binary_darwin.go in your GoProjects/pkg
  2. currently gomonkey version is v2@v2.9.0, please replace this file with code below directly
    
    package gomonkey

import ( "syscall" "time" )

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) }

func mprotectCrossPage(addr uintptr, length int, prot int) error { pageSize := syscall.Getpagesize() for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) { page := entryAddress(p, pageSize) if err := syscall.Mprotect(page, prot); err != nil { return err } } return nil }

719010539 commented 1 year ago

收到!

aloisio31 commented 1 year ago

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

this works for me!

719010539 commented 1 year ago

收到!

Cronusl commented 1 year ago

get error on m1, could not import time (open : no such file or directory)

vimt commented 1 year ago

Test ok at Mac M1 13.2.1 Please follow step below to solve problem "permission denied" follow this issues steam solution and test at Mac M1.

  • Enjoy it and save your life, I donnot know why!
  1. find this file modify_binary_darwin.go in your GoProjects/pkg
  2. currently gomonkey version is v2@v2.9.0, please replace this file with code below directly
package gomonkey

import (
  "syscall"
  "time"
)

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)
}

func mprotectCrossPage(addr uintptr, length int, prot int) error {
  pageSize := syscall.Getpagesize()
  for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) {
      page := entryAddress(p, pageSize)
      if err := syscall.Mprotect(page, prot); err != nil {
          return err
      }
  }
  return nil
}

Not work for me :(

My env: M1 Pro, 13.3.1, Go, go1.20.1 darwin/arm64

I tried with this file

package main

import (
    "fmt"
    "github.com/agiledragon/gomonkey/v2"
)

//go:noinline
func Foo() {
    fmt.Println("not patch")
}

func main() {
    gomonkey.ApplyFunc(Foo, func() {
        fmt.Println("patched")
    })
    Foo()
}

when I run this file, I got segmentation fault

$ go run test_gomonkey.go 
signal: segmentation fault
FLAGLORD commented 1 year ago

Test ok at Mac M1 13.2.1 Please follow step below to solve problem "permission denied" follow this issues steam solution and test at Mac M1.

  • Enjoy it and save your life, I donnot know why!
  1. find this file modify_binary_darwin.go in your GoProjects/pkg
  2. currently gomonkey version is v2@v2.9.0, please replace this file with code below directly
package gomonkey

import (
  "syscall"
  "time"
)

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)
}

func mprotectCrossPage(addr uintptr, length int, prot int) error {
  pageSize := syscall.Getpagesize()
  for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) {
      page := entryAddress(p, pageSize)
      if err := syscall.Mprotect(page, prot); err != nil {
          return err
      }
  }
  return nil
}

It works for me, but I can only use it when ARCH is arm64. If I switch to amd64, it produces the same error. It may introduce compatibility problems

xialeistudio commented 1 year ago

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.

Works for my M1, thank you very much!

719010539 commented 1 year ago

收到!

spacewander commented 1 year ago

@agiledragon Would you like to apply this fix to the code? It's cumbersome to apply this patch by the user each time we use gomonkey.

719010539 commented 1 year ago

收到!

cavanwang commented 10 months ago

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

It works fine for me on mac os with AMD64.

719010539 commented 10 months ago

收到!

kangour commented 9 months ago

m2 同样有这个问题。 go1.21.0 darwin/arm64 gomonkey/v2 v2.3.1

719010539 commented 9 months ago

收到!

Zjmainstay commented 9 months ago

Test ok at Mac M1 13.2.1 Please follow step below to solve problem "permission denied" follow this issues steam solution and test at Mac M1.

  • Enjoy it and save your life, I donnot know why!
  1. find this file modify_binary_darwin.go in your GoProjects/pkg
  2. currently gomonkey version is v2@v2.9.0, please replace this file with code below directly
package gomonkey

import (
  "syscall"
  "time"
)

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)
}

func mprotectCrossPage(addr uintptr, length int, prot int) error {
  pageSize := syscall.Getpagesize()
  for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) {
      page := entryAddress(p, pageSize)
      if err := syscall.Mprotect(page, prot); err != nil {
          return err
      }
  }
  return nil
}

这个测试可用,但是这里的表述有点问题,我补充一下: 1、只需要修改文件modify_binary_darwin.go第7行,删除参数:syscall.PROT_EXEC 最终:err := mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_WRITE) 2、使用go test -gcflags=all=-l 进行测试

2264381489 commented 9 months ago

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

its good for me

zplzpl commented 8 months ago

Test ok at Mac M1 13.2.1 Please follow step below to solve problem "permission denied" follow this issues steam solution and test at Mac M1.

  • Enjoy it and save your life, I donnot know why!
  1. find this file modify_binary_darwin.go in your GoProjects/pkg
  2. currently gomonkey version is v2@v2.9.0, please replace this file with code below directly
package gomonkey

import (
    "syscall"
    "time"
)

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)
}

func mprotectCrossPage(addr uintptr, length int, prot int) error {
    pageSize := syscall.Getpagesize()
    for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) {
        page := entryAddress(p, pageSize)
        if err := syscall.Mprotect(page, prot); err != nil {
            return err
        }
    }
    return nil
}

这个测试可用,但是这里的表述有点问题,我补充一下: 1、只需要修改文件modify_binary_darwin.go第7行,删除参数:syscall.PROT_EXEC 最终:err := mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_WRITE) 2、使用go test -gcflags=all=-l 进行测试

work for me