mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
OSX/MACOS: Fix crash when toggling Suspend Screensaver
-[RetroArch_OSX setDisableDisplaySleep:] stored the NSActivity token returned by -beginActivityWithOptions:reason: in a raw-id ivar with no retain. The token is autoreleased and owned by the system, so under MRR (used by RetroArch_PPC.xcodeproj and the qb/make build - ARC is not universal in this file) it was deallocated as soon as the surrounding autorelease pool drained, leaving _sleepActivity as a dangling pointer. Toggling the setting back off then sent -endActivity: to freed memory, which crashed in objc_opt_isKindOfClass at offset 0x1c while checking -isKindOfClass:_NSActivityAssertion. Fixes (macOS 12.3, x86_64, 1.22.2): 0 libobjc objc_opt_isKindOfClass + 27 1 Foundation -[NSProcessInfo endActivity:] + 29 2 RetroArch -[RetroArch_OSX setDisableDisplaySleep:] + 125 3 RetroArch general_write_handler + 1646 4 RetroArch setting_bool_action_ok_default + 34 ... - Retain the token (RARCH_RETAIN, no-op under ARC, -retain under MRR) so it survives across the begin/end pair. - Capture + nil-out the ivar before calling -endActivity: to make a re-entrant call from inside AppKit's menu write path unable to observe a stale pointer and double-end. - Release any outstanding activity in -dealloc (MRR branch) so quitting while the screensaver is suspended does not leak the system-wide display-sleep assertion. - Guard the whole method with MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 so 10.5-era SDKs (Xcode 3.1 / GCC 4.0, which predate both App Nap and ARC) still compile, and with -respondsToSelector: so a binary built on a newer SDK but deployed to 10.5-10.8 no-ops instead of crashing on unrecognized selector. Also adds RARCH_RETAIN to cocoa_defines.h next to the existing RARCH_RELEASE / RARCH_AUTORELEASE / RARCH_SUPER_DEALLOC family.
This commit is contained in:
1 parent
b0f6690073
commit
9743d1e1c3
2 files changed
+54
-2
No files matched your search
@@ -93,10 +93,12 @@
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#define RARCH_RETAIN(x) (x)
|
||||
#define RARCH_RELEASE(x) ((void)0)
|
||||
#define RARCH_AUTORELEASE(x) ((void)0)
|
||||
#define RARCH_SUPER_DEALLOC() ((void)0)
|
||||
#else
|
||||
#define RARCH_RETAIN(x) [(x) retain]
|
||||
#define RARCH_RELEASE(x) [(x) release]
|
||||
#define RARCH_AUTORELEASE(x) [(x) autorelease]
|
||||
#define RARCH_SUPER_DEALLOC() [super dealloc]
|
||||
|
||||
+52
-2
@@ -615,6 +615,22 @@ static ui_application_t ui_application_cocoa = {
|
||||
* _window manually. See cocoa_defines.h for the ARC/MRR macro story. */
|
||||
- (void)dealloc
|
||||
{
|
||||
/* Make sure any outstanding NSProcessInfo activity is ended and
|
||||
* released. Without this, quitting while the screensaver is
|
||||
* suspended would leak both the activity assertion (leaving
|
||||
* display-sleep disabled system-wide until reboot) and the
|
||||
* retained token itself. */
|
||||
#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
|
||||
if (_sleepActivity)
|
||||
{
|
||||
NSProcessInfo *pi = [NSProcessInfo processInfo];
|
||||
id token = _sleepActivity;
|
||||
_sleepActivity = nil;
|
||||
if ([pi respondsToSelector:@selector(endActivity:)])
|
||||
[pi endActivity:token];
|
||||
RARCH_RELEASE(token);
|
||||
}
|
||||
#endif
|
||||
RARCH_RELEASE(_window);
|
||||
RARCH_SUPER_DEALLOC();
|
||||
}
|
||||
@@ -808,20 +824,54 @@ static ui_application_t ui_application_cocoa = {
|
||||
|
||||
- (bool)setDisableDisplaySleep:(bool)disable
|
||||
{
|
||||
/* beginActivityWithOptions:reason: / endActivity: were added in
|
||||
* macOS 10.9. Guard at compile time so builds against older SDKs
|
||||
* (notably 10.5 via Xcode 3.1 / GCC 4.0, which predate both App
|
||||
* Nap and ARC) still compile, and guard at runtime so a binary
|
||||
* built against a 10.9+ SDK but deployed onto 10.5-10.8 gracefully
|
||||
* no-ops instead of crashing on an unrecognized selector. */
|
||||
#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
|
||||
NSProcessInfo *pi = [NSProcessInfo processInfo];
|
||||
if (![pi respondsToSelector:@selector(beginActivityWithOptions:reason:)])
|
||||
return NO;
|
||||
|
||||
if (disable)
|
||||
{
|
||||
if (_sleepActivity == nil)
|
||||
_sleepActivity = [NSProcessInfo.processInfo beginActivityWithOptions:NSActivityIdleDisplaySleepDisabled reason:@"disable screen saver"];
|
||||
{
|
||||
/* The token returned by beginActivityWithOptions:reason: is
|
||||
* autoreleased and owned by the system. We MUST retain it
|
||||
* or, under MRR, it is deallocated as soon as the current
|
||||
* autorelease pool drains - leaving _sleepActivity as a
|
||||
* dangling pointer. The next call to endActivity: then
|
||||
* sends isKindOfClass: to freed memory and crashes in
|
||||
* objc_opt_isKindOfClass. Under ARC a plain `id` ivar is
|
||||
* implicitly __strong so RARCH_RETAIN is a no-op; under MRR
|
||||
* it expands to an explicit -retain. */
|
||||
id token = [pi beginActivityWithOptions:NSActivityIdleDisplaySleepDisabled
|
||||
reason:@"disable screen saver"];
|
||||
_sleepActivity = RARCH_RETAIN(token);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_sleepActivity)
|
||||
{
|
||||
[NSProcessInfo.processInfo endActivity:_sleepActivity];
|
||||
/* Capture and nil-out BEFORE calling endActivity: so that a
|
||||
* re-entrant call (for example from a menu write handler
|
||||
* triggered while AppKit is dispatching) cannot observe a
|
||||
* stale pointer and double-end the same activity. */
|
||||
id token = _sleepActivity;
|
||||
_sleepActivity = nil;
|
||||
[pi endActivity:token];
|
||||
RARCH_RELEASE(token);
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
#else
|
||||
(void)disable;
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Reference in new issue
Block a user