Skip to content

macOS: fatal error: invalid pointer found on stack in cScreenToScreen on display change (UTF8String use-after-free) #5556

@AndreyKizimenko

Description

@AndreyKizimenko

Description

On macOS, processScreen (in pkg/application/screen_darwin.go) stores the result of -[NSString UTF8String] directly into the C Screen struct's id and name fields:

// screen_darwin.go (CGO preamble)
returnScreen.id   = [[NSString stringWithFormat:@"%d", displayID] UTF8String]; // line ~71
...
returnScreen.name = [screen.localizedName UTF8String];                          // line ~85

-[NSString UTF8String] returns a pointer to an autoreleased buffer whose lifetime is bound to the current autorelease pool. The Screen structs are malloc'd in getAllScreens() and returned to Go, which reads the strings later in cScreenToScreen:

ID:   C.GoString(screen.id),
Name: C.GoString(screen.name),  // line ~186

By the time Go calls C.GoString, the autorelease pool has typically drained and the underlying UTF-8 buffer has been freed. screen.name/screen.id are then dangling pointers. Most of the time the freed memory still happens to hold the old bytes, so it appears to work; intermittently it points at reused/garbage memory and GoString dereferences an invalid address.

This is a use-after-free. There is also a secondary latent bug in processAndCacheScreens: getAllScreens() sizes its malloc from [[NSScreen screens] count], while numScreens := int(C.GetNumScreens()) queries [[NSScreen screens] count] again in a separate call. If the display configuration changes between the two calls, the loop can index past the allocated array.

Crash

Triggered by Wails' own internal handler for events.Mac.ApplicationDidChangeScreenParameters (registered in application_darwin.go), which fires on display sleep/wake, resolution changes, and monitor connect/disconnect:

fatal error: invalid pointer found on stack

runtime.gostring(0x3)
internal/bytealg.IndexByteString()
runtime.findnull(...)
github.com/wailsapp/wails/v3/pkg/application._Cfunc_GoString(...)
github.com/wailsapp/wails/v3/pkg/application.cScreenToScreen(...)            screen_darwin.go:186
github.com/wailsapp/wails/v3/pkg/application.(*macosApp).processAndCacheScreens(...) screen_darwin.go:199
github.com/wailsapp/wails/v3/pkg/application.(*macosApp).run.func3(...)      application_darwin.go:327
github.com/wailsapp/wails/v3/pkg/application.(*EventManager).handleApplicationEvent.func1() event_manager.go:171

Because it is a fatal error (a runtime memory-safety abort, not a Go panic), it cannot be recovered with recover(), and the triggering event handler is internal to Wails so applications cannot intercept it.

To reproduce

  1. Run any Wails v3 app on macOS that stays open for an extended period.
  2. Let the display sleep/wake a few times, or connect/disconnect an external monitor (anything that emits NSApplicationDidChangeScreenParametersNotification).
  3. Intermittently, the app aborts with fatal error: invalid pointer found on stack in cScreenToScreen.

Observed once over an ~11h session on macOS (Apple Silicon), Go 1.26.1, Wails v3.0.0-alpha.98.

Suggested fix

Don't store the autoreleased UTF8String pointer. Either:

  • strdup() the result in processScreen and free() it after C.GoString (e.g. in cScreenToScreen / after processAndCacheScreens), or
  • copy into fixed-size char[] fields inside the Screen struct.

Separately, make processAndCacheScreens use a single screen snapshot — e.g. have getAllScreens() return its element count (or a NULL terminator) rather than calling GetNumScreens() a second time.

Environment

  • Wails CLI / library: v3.0.0-alpha.98
  • Platform: macOS (Apple Silicon)
  • Go: 1.26.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't workingMacOSv3

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions