mattn/go-pointer

go vet: misuse of unsafe.Pointer

Closed this issue · 2 comments

  1. My system

    Ubuntu 14.04.3 LTS \n \l
    Linux ... 3.13.0-76-generic #120-Ubuntu SMP Mon Jan 18 15:59:10 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
    
    go version go1.7 linux/amd64
    
  2. The problem is with Go runtime

    go vet github.com/mattn/go-pointer
    src/github.com/mattn/go-pointer/pointer.go:20: possible misuse of unsafe.Pointer
    exit status 1
    

    If I go vet the lib, I get the described error. After some research, I have found that one should not cast integers to pointer, with the result where we end up with invalid dangling pointer.

    See https://golang.org/doc/go1.3 section "Changes to the garbage collector"

    From described I expect, that this code can be problematic.

  3. Possible solution would, be to make index pointer actually valid:

This seems to work:

// based on https://github.com/mattn/go-pointer
package main

//#include <stdlib.h>
import "C"
import (
    "sync"
    "unsafe"
)

var (
    cgoPointerHackLock sync.Mutex
    cgoPointerHackMap  map[unsafe.Pointer]interface{} = map[unsafe.Pointer]interface{}{}
)

// Since Go 1.6, cgo argument can't be passed Go pointer. (see https://golang.org/doc/go1.6#cgo )
// Trick here is to store Go data links inside map, which is indexed by valid C pointers.
// C memory allocated for indexing is not really used, but should satisfy request,
// that unsafe.Pointer should always point to valid data. (see (Changes to GC section): https://golang.org/doc/go1.3 )
func cgoPointerHackSave(v interface{}) unsafe.Pointer {
    if v == nil {
        return nil
    }
    var ptr unsafe.Pointer
    // find next available pointer
    cgoPointerHackLock.Lock()
    // Generate real fake C pointer.
    // This pointer will not store any data, but will bi used for indexing purposes.
    // Since Go doest allow to cast dangling pointer to unsafe.Pointer, we do rally allocate one byte.
    // Why we need indexin, because Go doest allow C code to store pointers to Go data.
    ptr = C.malloc(C.size_t(1))
    if ptr == nil {
        cgoPointerHackLock.Unlock()
        panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil")
        //return
    }
    cgoPointerHackMap[ptr] = v
    cgoPointerHackLock.Unlock()
    return ptr
}

func cgoPointerHackGet(ptr unsafe.Pointer, unref bool) interface{} {
    if ptr == nil {
        return nil
    }
    cgoPointerHackLock.Lock()
    if v, ok := cgoPointerHackMap[ptr]; ok {
        if unref {
            delete(cgoPointerHackMap, ptr)
            C.free(ptr)
        }
        cgoPointerHackLock.Unlock()
        return v
    }
    cgoPointerHackLock.Unlock()
    return nil
}

func cgoPointerHackUnref(ptr unsafe.Pointer) {
    if ptr == nil {
        return
    }
    cgoPointerHackLock.Lock()
    delete(cgoPointerHackMap, ptr)
    C.free(ptr)
    cgoPointerHackLock.Unlock()
}
mattn commented

good catch. thanks.

2342232

No problem.

Your trick proved very very useful with GTK and webkit2gtk callbacks and signals :D
Thank you for that.