Skip to content

Conversation

@ddevidchenko
Copy link

Summary: Fixed Memory Corruption Panic in Async Callbacks

The Bug

The library was misusing cgo.Handle in every asynchronous function (e.g., MapAsync, RequestAdapter, RequestDevice, and internal error scope checks).

The code was creating a cgo.Handle and then passing a pointer to that local variable (unsafe.Pointer(&handle)) to a C function. Since these functions are asynchronous, the Go function returns immediately and its stack frame is destroyed. When the C callback eventually fires, it attempts to dereference that now-invalid stack pointer to retrieve the handle, leading to:
panic: runtime/cgo: misuse of an invalid Handle

Affected Platforms

This issue likely affects all desktop platforms (macOS, Windows, Linux) where Go's CGO implementation is used. It was specifically observed and reproduced on macOS.

How to Reproduce

  1. Use any asynchronous function in the library, such as Buffer.MapAsync.
  2. Perform frequent asynchronous operations (e.g., reading back data every frame).
  3. Within 10-20 seconds, the application will crash with a runtime/cgo panic when a callback attempts to process an invalid pointer from a previous call's stack.

The Fix

The fix implements the standard, safe pattern for passing cgo.Handle through C:

  1. Pass by Value: Instead of passing the address of the handle variable, the handle (which is a uintptr under the hood) is converted directly to an unsafe.Pointer and passed as the userdata.

    // Before (BROKEN)
    handle := cgo.NewHandle(cb)
    C.some_async_func(..., unsafe.Pointer(&handle))
    
    // After (FIXED)
    handle := cgo.NewHandle(cb)
    C.some_async_func(..., unsafe.Pointer(handle))
  2. Cast on Recovery: In the Go callback exported to C, the userdata is cast back to a cgo.Handle directly without dereferencing a pointer.

    // Before (BROKEN)
    handle := *(*cgo.Handle)(userdata)
    
    // After (FIXED)
    handle := cgo.Handle(userdata)

This ensures that the handle value itself is preserved in the C userdata field, independent of the lifetime of the calling function's stack.

Copy link
Member

@kkoreilly kkoreilly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution!

I think that this fix for #6 might be superseded by #13, which has a more robust implementation of handles. We can see if your issue is addressed once we merge #13; if it is not, I will take another look at this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants