There is a race condition between Go’s Garbage Collector and Cgo execution. The library currently suffers from intermittent SIGSEGV crashes (as seen in the stack trace) because Go finalizers are being triggered for Parent objects while Child objects are still accessing their memory via Cgo.
The trace shows Goroutine 35 (Go's internal Finalizer goroutine) calling lbug_query_result_destroy while Goroutine 133 is simultaneously executing lbug_flat_tuple_get_value. This results in a Use-After-Free crash because the QueryResult's memory buffer was freed by the finalizer while the FlatTuple was still reading from it
Trunacted stack trace:
goroutine 35 gp=0x14000180540 m=8 [syscall]:
runtime.cgocall(0x10537c1b8, 0x1400006e518)
github.com/LadybugDB/go-ladybug._Cfunc_lbug_query_result_destroy(0x14000825540)
github.com/LadybugDB/go-ladybug.(*QueryResult).Close.func1(...)
/go-ladybug@v0.13.1/query_result.go:39
goroutine 133 gp=0x14000181880 m=nil [runnable]:
github.com/LadybugDB/go-ladybug.lbugValueToGoValue({0x152a23d38, ...})
/go-ladybug@v0.13.1/value_helper.go:261
github.com/LadybugDB/go-ladybug.(*FlatTuple).GetValue(0x14000c10200, 0x1)
/go-ladybug@v0.13.1/flat_tuple.go:77
Go's GC marks a QueryResult as unreachable as soon as the Go code no longer references it—even if a FlatTuple derived from it is still inside a C function. Go is unaware that the C-level FlatTuple depends on the QueryResult's memory.
Currently, finalizers are registered before checking the C return status (e.g., in QueryResult.Next() or OpenConnection()). If the C call fails the GC eventually collects this failed object calling the destroy function on a null or uninitialised handle and crashing.
There is a race condition between Go’s Garbage Collector and Cgo execution. The library currently suffers from intermittent SIGSEGV crashes (as seen in the stack trace) because Go finalizers are being triggered for Parent objects while Child objects are still accessing their memory via Cgo.
The trace shows Goroutine 35 (Go's internal Finalizer goroutine) calling lbug_query_result_destroy while Goroutine 133 is simultaneously executing lbug_flat_tuple_get_value. This results in a Use-After-Free crash because the QueryResult's memory buffer was freed by the finalizer while the FlatTuple was still reading from it
Trunacted stack trace:
Go's GC marks a QueryResult as unreachable as soon as the Go code no longer references it—even if a FlatTuple derived from it is still inside a C function. Go is unaware that the C-level FlatTuple depends on the QueryResult's memory.
Currently, finalizers are registered before checking the C return status (e.g., in
QueryResult.Next()orOpenConnection()). If the C call fails the GC eventually collects this failed object calling the destroy function on a null or uninitialised handle and crashing.