How to solve the "bad pointer in write barriers" problem in cgo when the C library uses opaque pointers to the structure

I am currently writing a Go wrapper around the C library. This C library uses opaque structure pointers to hide information through the interface. However, the base implementation retains the size_t values. This leads to errors during program execution. The minimum working example for reproducing the problem is as follows:

main.go:

package main

/*
#include "stddef.h"
// Create an opaque type to hide the details of the underlying data structure.
typedef struct HandlePrivate *Handle;

// In reality, the implementation uses a type derived from size_t for the Handle.
Handle getInvalidPointer() {
    size_t actualHandle = 1;
    return (Handle) actualHandle;
}
 */
import "C"

// Create a temporary slice containing invalid pointers.
// The idea is that the local variable slice can be garbage collected at the end of the function call.
// When the slice is scanned for linked objects, the GC comes across the invalid pointers.
func getTempSlice() {
    slice := make([]C.Handle, 1000000)
    for i, _ := range slice {
        slice[i] = C.getInvalidPointer()
    }
}

func main() {
    getTempSlice()
}

Running this program will result in the following error

runtime: writebarrierptr *0xc42006c000 = 0x1
fatal error: bad pointer in write barrier
[...stack trace omitted...]

Note that errors disappear when the GC is disabled by setting an environment variable GOGC=off.

- . , , , GC. , .

- Ubuntu 16.04, gcc 5.4.0 Go 1.9.2.

cgo

+4
1

go1.8.5 go1.9.2. : devel +f01b928 Sat Nov 11 06:17:48 2017 +0000 ( go1.10alpha).


// Create a temporary slice containing invalid pointers.
// The idea is that the local variable slice can be garbage collected at the end of the function call.
// When the slice is scanned for linked objects, the GC comes across the invalid pointers.

A Go . , , , GC . (go1.8.5 go1.9.2). undefined , , GC , (go devel).

Go , GC run run GC .

// go tool cgo
// type _Ctype_Handle *_Ctype_struct_HandlePrivate
// var handle _Ctype_Handle
var handle C.Handle
// main._Ctype_Handle <nil> 0x0
fmt.Fprintf(os.Stderr, "%[1]T %[1]v %[1]p\n", handle)

slice := make([]C.Handle, 1000000)
for i, _ := range slice {
    slice[i] = C.getInvalidPointer()
}

uintptr. ,

package main

import "unsafe"

/*
#include "stddef.h"
// Create an opaque type to hide the details of the underlying data structure.
typedef struct HandlePrivate *Handle;

// In reality, the implementation uses a type derived from size_t for the Handle.
Handle getInvalidPointer() {
    size_t actualHandle = 1;
    return (Handle) actualHandle;
}
*/
import "C"

// Create a temporary slice of C pointers as Go integer type uintptr.
func getTempSlice() {
    slice := make([]uintptr, 1000000)
    for i, _ := range slice {
        slice[i] = uintptr(unsafe.Pointer(C.getInvalidPointer()))
    }
}

func main() {
    getTempSlice()
}
+2

Source: https://habr.com/ru/post/1689112/


All Articles