frida / frida-go

Frida Go bindings
https://frida.re/
ISC License
157 stars 19 forks source link

SIGSEGV: segmentation violation #22

Open jackie575 opened 11 months ago

jackie575 commented 11 months ago

HI ,I encounter a problem when many concurrent goroutine execute Script.ExportsCall;Here is The Env And Error Info

OS:  Mac Os  14.1.2 (23B92)
CPU ARCH:  2.6 GHz  Intel Core i7 Amd64

Go Version: go1.21.5
Frida-go-sdk version:  v0.6.10

Android Device Version : Android 12 
Android frida-server version :  16.1.4 Arm64

Updated:  switch the latest Frida-Server 16.1.8 , the problem reproduced

===========

Error Stack:
SIGSEGV: segmentation violation
PC=0x1010b21c2 m=15 sigcode=1
signal arrived during cgo execution

goroutine 2986 [syscall]:
runtime.cgocall(0x100a46800, 0xc00129ae58)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/cgocall.go:157 +0x4b fp=0xc00129ae30 sp=0xc00129adf8 pc=0x10000aaab
github.com/frida/frida-go/frida._Cfunc_g_object_unref(0x7fb31d817380)
        _cgo_gotypes.go:3402 +0x3f fp=0xc00129ae58 sp=0xc00129ae30 pc=0x1002e71df
github.com/frida/frida-go/frida.unrefGObj.func1(0x7fb31d817380)
        /Users/jackie/go/pkg/mod/github.com/frida/frida-go@v0.6.10/frida/cleanups.go:30 +0x3b fp=0xc00129ae90 sp=0xc00129ae58 pc=0x1002eb31b
github.com/frida/frida-go/frida.unrefGObj(0x7fb31d817380)
        /Users/jackie/go/pkg/mod/github.com/frida/frida-go@v0.6.10/frida/cleanups.go:30 +0x18 fp=0xc00129aea8 sp=0xc00129ae90 pc=0x1002eb2b8
github.com/frida/frida-go/frida.clean(0x7fb31d817380, {0x1012f6734, 0xb})
        /Users/jackie/go/pkg/mod/github.com/frida/frida-go@v0.6.10/frida/cleanups.go:37 +0x5e fp=0xc00129aee0 sp=0xc00129aea8 pc=0x1002eb39e
github.com/frida/frida-go/frida.(*Session).Clean(0xc000015900)
        /Users/jackie/go/pkg/mod/github.com/frida/frida-go@v0.6.10/frida/session.go:198 +0x33 fp=0xc00129af10 sp=0xc00129aee0 pc=0x100302d33
frida-go-rpc/MyFridaApp/fridaApiHooker.close({0xc000015800, 0xc0000158a8, 0xc000015900, 0xc0001f02a0})
        /GoProjects/frida-go-rpc/MyApp/ApiHooker.go:35 +0x3c fp=0xc00129af38 sp=0xc00129af10 pc=0x1007bd41c
frida-go-rpc/MyFridaApp/.GetClient.func1.1.1()
        /GoProjects/frida-go-rpc/MyFridaApp/FridaClient.go:158 +0x9e fp=0xc00129afe0 sp=0xc00129af38 pc=0x1007bf91e
runtime.goexit()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc00129afe8 sp=0xc00129afe0 pc=0x1000785e1
created by frida-go-rpc/MyFridaApp/.GetClient.func1.1 in goroutine 5
        /GoProjects/frida-go-rpc/MyFridaApp/FridaClient.go:148 +0x145

goroutine 1 [sleep]:
runtime.gopark(0x10135a680, 0xc0002300f0, 0x13, 0x13, 0x1)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:398 +0xfc fp=0xc001491b60 sp=0xc001491b30 pc=0x10004673c
time.Sleep(0x174876e800)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/time.go:195 +0x110 fp=0xc001491ba0 sp=0xc001491b60 pc=0x100075550
main.main()
        /GoProjects/frida-go-rpc/main.go:213 +0x12a fp=0xc001491f68 sp=0xc001491ba0 pc=0x100a4474a
runtime.main()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:267 +0x267 fp=0xc001491fe0 sp=0xc001491f68 pc=0x1000462c7
runtime.goexit()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc001491fe8 sp=0xc001491fe0 pc=0x1000785e1

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc0015a5fe8 sp=0xc0015a5fe0 pc=0x1000785e1

goroutine 2 [force gc (idle)]:
runtime.gopark(0x10135a640, 0x103fb1d60, 0x11, 0x14, 0x1)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:398 +0xfc fp=0xc00006af80 sp=0xc00006af50 pc=0x10004673c
runtime.goparkunlock(0x0?, 0x0?, 0x0?, 0x0?)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:404 +0x25 fp=0xc00006afb0 sp=0xc00006af80 pc=0x1000467c5
runtime.forcegchelper()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:322 +0xb5 fp=0xc00006afe0 sp=0xc00006afb0 pc=0x100046555
runtime.goexit()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc00006afe8 sp=0xc00006afe0 pc=0x1000785e1
created by runtime.init.6 in goroutine 1
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:310 +0x1a

goroutine 18 [GC sweep wait]:
runtime.gopark(0x10135a640, 0x103fb2ba0, 0xc, 0x14, 0x1)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:398 +0xfc fp=0xc000066750 sp=0xc000066720 pc=0x10004673c
runtime.goparkunlock(0x1?, 0x0?, 0x0?, 0x0?)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:404 +0x25 fp=0xc000066780 sp=0xc000066750 pc=0x1000467c5
runtime.bgsweep(0x0?)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mgcsweep.go:321 +0xe5 fp=0xc0000667c8 sp=0xc000066780 pc=0x10002f525
runtime.gcenable.func1()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mgc.go:200 +0x25 fp=0xc0000667e0 sp=0xc0000667c8 pc=0x1000237a5
runtime.goexit()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc0000667e8 sp=0xc0000667e0 pc=0x1000785e1
created by runtime.gcenable in goroutine 1
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mgc.go:200 +0x66

goroutine 19 [GC scavenge wait]:
runtime.gopark(0x10135a640, 0x103fb33e0, 0xd, 0x14, 0x2)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:398 +0xfc fp=0xc000066f40 sp=0xc000066f10 pc=0x10004673c
runtime.goparkunlock(0x1013a5288?, 0x0?, 0x0?, 0x0?)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:404 +0x25 fp=0xc000066f70 sp=0xc000066f40 pc=0x1000467c5
runtime.(*scavengerState).park(0x103fb33e0)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mgcscavenge.go:425 +0x45 fp=0xc000066f98 sp=0xc000066f70 pc=0x10002c7e5
runtime.bgscavenge(0x0?)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mgcscavenge.go:658 +0x65 fp=0xc000066fc8 sp=0xc000066f98 pc=0x10002cd85
runtime.gcenable.func2()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mgc.go:201 +0x25 fp=0xc000066fe0 sp=0xc000066fc8 pc=0x100023745
runtime.goexit()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc000066fe8 sp=0xc000066fe0 pc=0x1000785e1
created by runtime.gcenable in goroutine 1
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mgc.go:201 +0xa5

goroutine 3 [finalizer wait]:
runtime.gopark(0x10135a370, 0x103feeda8, 0x10, 0x14, 0x1)
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/proc.go:398 +0xfc fp=0xc00006a628 sp=0xc00006a5f8 pc=0x10004673c
runtime.runfinq()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mfinal.go:193 +0xfa fp=0xc00006a7e0 sp=0xc00006a628 pc=0x10002285a
runtime.goexit()
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/asm_amd64.s:1650 +0x1 fp=0xc00006a7e8 sp=0xc00006a7e0 pc=0x1000785e1
created by runtime.createfing in goroutine 1
        /usr/local/Cellar/go/1.21.5/libexec/src/runtime/mfinal.goExiting.

Any Other Info ,when the app call Script.ExportsCall Timeout, i think it is just broken Session,so i close the resource , reopen another DeviceManager 、Device、Session and so on; Here is the code

hooker.script.Clean()
hooker.session.Clean()
hooker.device.Clean()
hooker.deviceManager.Clean()

Can anyone help me to solve it? Many Thanks!

NSEcho commented 11 months ago

Hey, can you paste the code that caused the error

jackie575 commented 11 months ago

Hey, can you paste the code that caused the error

Thanks For Your Reply!

Here is the code :

type FridaClient struct {

    apiHooker *fridaApiHooker
    // 
    refreshChan *chan uint32
    // Client Version,
    version * atomic.Uint32
    // Frida Resource is ok?
    OK bool
}

type fridaApiHooker struct {

    deviceManager *frida.DeviceManager

    device *frida.Device

    session *frida.Session

    script *frida.Script
}

// Close close the resource held by the frida client singleton
func (hooker fridaApiHooker) close()  {

    hooker.script.Clean()

    hooker.session.Clean()

    hooker.device.Clean()

    hooker.deviceManager.Clean()

}

// newApiHooker make new frida Resource
func newApiHooker() *fridaApiHooker {

    jsScript := readJsScript()

    deviceManager := frida.NewDeviceManager()

    devices, err := deviceManager.EnumerateDevices()
    if err != nil {

        panic("Device Error!")
    }

    for _, d := range devices {
        log.Printf("[*] Found device with id:%v", d.ID())
    }

    device, err := deviceManager.USBDevice()
    if err != nil {

        log.Printf("Could not get usb device: %v", err)

        os.Exit(1)
    }

    log.Printf("[*] Attaching to `App`")

    session, err := device.Attach(processName, nil)
    if err != nil {

        log.Printf("New Session Fail!err:%v", err)
        os.Exit(1)
    }

    fridaScript, err := session.CreateScript(jsScript)
    if err != nil {

        log.Printf("New Frida Script Fail! err:%v", err)
        os.Exit(1)
    }

    if err := fridaScript.Load(); err != nil {

        log.Printf("Load Frida Script Fail!err:%v", err)
        os.Exit(1)
    }

    return &fridaApiHooker{
        deviceManager: deviceManager,
        device:        device,
        session:       session,
        script:   fridaScript,
    }
}

func (client * FridaClient) CallFrida(param *CallFridaParam) (*rpc.CallResult, error) {
    // for loop 3 times
    for i := 0; i < 3; i++ {
        // every iteration , call the func , if err is TimeOutError,close
        // the resource,  reopen it 
        result, err := client.doFridaCallInner(param)

        if err != nil {

            if errors.Is(err, timeoutError) {
                // Timeout Error,get Client Version
                ver := client.version.Load()
                *client.refreshChan <- ver

                // wait for the client version greater than current,and 'ok'
                // statuc switch to 'true'

                for !(client.version.Load() > ver && client.OK) {
                    // wait for reloader to reload resource
                    time.Sleep(1 * time.Second)
                }

                continue
            } else {
                // other error than TimeOut
                return nil, err
            }
        } else {
            // call frida success ,just return it
            return result, nil
        }
    }

    // for loop end ,can't get result ,return err
    return nil, timeoutError

}

func (client *FridaClient) doFridaCallInner(param *FridaCallParam) (*rpc.FridaResult, error) {
    // make chan to recv result
    ch := make(chan FridaResult, 1)

    go func() {

        defer close(ch)

        resultMap := client.apiHooker.script.ExportsCall("JSFuncName",
            // 
            params
        )

        ch <- FridaResult{resultMap, nil}

    }()
    // 4. 当前协程等待结果
    select {
    case <-time.After(RpcTimeout * time.Second):
        {
            // 
            return nil, timeoutError
        }
    case result := <-ch:
        {
            return &result,nil
        }
    }
}

// GetFridaClient make FridaClient Singleton
func GetFridaClient() *FridaClient {

    clientInitLock.Do(func() {
        log.Printf("init  Frida client! ")

        apiHooker := newApiHooker()

        ch := make(chan uint32, 1)

        ver := atomic.Uint32{}
        // incr to version 1 
        ver.Add(1)
        clientSingleton = &FridaClient{
            apiHooker: apiHooker,
            //  version init to 1
            version: &ver,
            // size 1 channel 
            refreshChan: &ch,
            // 
            OK: true,
        }
        // 7. launch a new coroutine , do for refresh Resource
        go func() {
            for {
                // wait for channel recv 
                v := <-*clientSingleton.refreshChan

                if v == clientSingleton.version.Load() {
                    //  version equals ,do the resource refresh actions
                    casSuccess := clientSingleton.version.CompareAndSwap(v, v+1)
                    if casSuccess {
                        go func() {
                            log.Printf("resource clean up")
                            // when cas success ,refresh resource 
                            clientSingleton.OK = false

                            defer func() { clientSingleton.OK = true }()

                            _ = clientSingleton.apiHooker.close()

                            // make new hooker ,update the prop of singleton
                            clientSingleton.apiHooker = newApiHooker()
                        }()
                    }
                } else {
                    // Version different,Just return
                    log.Printf("", v, clientSingleton.version)
                }
            }
        }()
    })

    return clientSingleton
}