frida / frida-go

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

SIGSEGV: segmentation violation #22

Open jackie575 opened 9 months ago

jackie575 commented 9 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 9 months ago

Hey, can you paste the code that caused the error

jackie575 commented 9 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
}