Repository navigation
Conversation
…atomically Fixes a SIGSEGV in C.GoString during display-reconfiguration event storms (typically on sleep/wake), caused by two memory-safety issues in screen_darwin.go: 1. Dangling C strings: processScreen stored the raw result of -[NSString UTF8String] (a pointer owned by the autoreleased NSString) into the Screen struct, which Go reads later via C.GoString. Once the NSString is deallocated the pointer dangles. Fixed by strdup'ing the UTF-8 bytes (copyCString) and freeing them on the Go side after conversion in cScreenToScreen, keeping every caller leak-free. 2. Count/allocation race: getAllScreens malloc'd the array from one [NSScreen screens] snapshot while processAndCacheScreens fetched the element count via a separate GetNumScreens call. If the display configuration changed between the two calls (exactly when these code paths run), the loop over-read the allocation. Fixed by returning the count from the same snapshot through an out-parameter. Also zero-initializes the Screen struct and guarantees name/id are always owned, non-NULL pointers (empty string when localizedName is unavailable or the NSScreen is nil), removing a C.GoString(NULL) hazard via the getScreenForWindow / getScreenForSystemTray paths. Observed crash (v3.0.0-alpha.97, macOS 26.5, arm64): runtime.gostring(0x10000001b) application._Cfunc_GoString application.cScreenToScreen screen_darwin.go:185 application.(*macosApp).processAndCacheScreens application.(*EventManager).handleApplicationEvent.func1
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughScreen enumeration on macOS now uses explicit C heap ownership for string fields and atomic screen-count acquisition. A new ChangesDarwin Screen Enumeration Memory Safety
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thanks for this, @x-haose — you correctly spotted that macOS screen enumeration could hand back pointers into Objective-C strings that get freed, and reading the screen count separately from the list was a real race. Solid catch. The same fix landed just ahead of this via #5565, which added (Note for posterity: #5516 tackles the same area from a different angle — marshalling enumeration onto the main thread — so that protection is being tracked separately.) |
…PRs (#5683) * docs(changelog): credit @puneetdixit200 for GTK4 menu fix (#5539) * docs(changelog): credit @x-haose for macOS screen-enumeration fix (#5584) --------- Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Description
Fixes a hard crash (
SIGSEGVinC.GoString) that occurs during display-reconfiguration event storms — typically when a Mac wakes from sleep, connects/disconnects a monitor, or changes resolution while a v3 app is running.Crash observed (v3.0.0-alpha.97, macOS 26.5, arm64)
The app had been running ~12.5h and crashed on wake.
Root causes (two memory-safety issues in
screen_darwin.go)Dangling C strings.
processScreenstores the raw result of-[NSString UTF8String]into theScreenstruct:That pointer is owned by the (autoreleased)
NSString. Go reads it later viaC.GoString— once the string is deallocated, the pointer dangles.0x10000001babove is exactly such a garbage pointer.Count/allocation race.
getAllScreens()mallocs the array from one[NSScreen screens]snapshot, whileprocessAndCacheScreensfetches the element count via a separateGetNumScreens()call. If the display configuration changes between the two calls (precisely when these code paths run), the loop over-reads the allocation.Fix
copyCStringhelper:strdupthe UTF-8 bytes; the Go side frees them after conversion incScreenToScreen, keeping everyprocessScreencaller leak-free.getAllScreens(int* outCount): return the count from the same snapshot as the allocation.Screenstruct and guaranteeid/nameare always owned, non-NULL pointers (empty string whenlocalizedNameis unavailable or theNSScreenis nil) — this also removes a latentC.GoString(NULL)hazard via thegetScreenForWindow/getScreenForSystemTraypaths (e.g. window not currently on any screen).No public API changes;
getAllScreensis internal to the file.Testing
go build ./pkg/applicationon macOS 26.5 / arm64 / go1.25.go.mod replace; previously crashed on wake, will report back after soak.Type of change
Summary by CodeRabbit
Bug Fixes
Refactor