sparkle-project / Sparkle

A software update framework for macOS
https://sparkle-project.org
Other
7.41k stars 1.05k forks source link

Crash in HIToolbox on macOS 12 #2548

Closed rsms closed 5 months ago

rsms commented 5 months ago

Summary

After carefully following the documentation and integrating Sparkle in macOS app, we encounter a 100% reproducible crash in macOS dyld dlopen_from when Sparkle is enabled.

Here's how we integrate Sparkle:

static bool g_autoUpdateCanCheck = false;
static AppUpdaterDelegate* g_appUpdaterDelegate = nil;
static SPUStandardUpdaterController* g_appUpdaterController = nil;

@interface AppUpdaterDelegate : NSObject <SPUUpdaterDelegate>
@property (nonatomic) SPUStandardUpdaterController* updaterController;
@end
@implementation AppUpdaterDelegate
- (void)observeCanCheckForUpdates {
  [_updaterController.updater
    addObserver:self
    forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))
    options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)
    context:nil];
}
- (void)observeValueForKeyPath:(NSString*)keyPath
                      ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change
                       context:(void*)context
{
  if ([keyPath isEqualToString:NSStringFromSelector(@selector(canCheckForUpdates))]) {
    g_autoUpdateCanCheck = _updaterController.updater.canCheckForUpdates;
    dlog("[autoupdate] %s", g_autoUpdateCanCheck ? "available" : "unavailable");
  } else {
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  }
}
- (void)dealloc {
  @autoreleasepool {
    [_updaterController.updater removeObserver:self forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))];
  }
}
@end

void PBAppAutoUpdateEnable() {
  assert(g_appUpdaterDelegate == nil);
  @autoreleasepool {
    g_appUpdaterDelegate = [[AppUpdaterDelegate alloc] init];
    g_appUpdaterController = [[SPUStandardUpdaterController alloc]
        initWithStartingUpdater:NO
                updaterDelegate:g_appUpdaterDelegate
             userDriverDelegate:nil];
    g_appUpdaterDelegate.updaterController = g_appUpdaterController;
    SPUUpdater* updater = g_appUpdaterDelegate.updaterController.updater;
    updater.feedURL = [NSURL URLWithString:@"https://our-server/releases.xml"];
    [g_appUpdaterDelegate observeCanCheckForUpdates];
    [g_appUpdaterDelegate.updaterController startUpdater];
  }
}

//...

@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification*)note {
  //...
  PBAppAutoUpdateEnable();
  //...
}
@end

When starting our app, which is a regular Apple code-signed .app bundle, it immediately crashes when clicking on the main menu.

Stack trace from debugger session:

$ lldb Our.app/Contents/MacOS/Our
(lldb) r
# App starting, all is OK
# Click on main menu
Process 37230 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x300265e)
    frame #0: 0x00000001a513ebec libobjc.A.dylib`invocation function for block in flushCaches(objc_class*, char const*, bool (objc_class*) block_pointer) + 60
libobjc.A.dylib`invocation function for block in flushCaches(objc_class*, char const*, bool (objc_class*) block_pointer):
->  0x1a513ebec <+60>: ldrh   w8, [x19, #0x1e]
    0x1a513ebf0 <+64>: cbz    w8, 0x1a513ece0           ; <+304>
    0x1a513ebf4 <+68>: ldr    x9, [x19, #0x10]
    0x1a513ebf8 <+72>: adrp   x8, 12
Target 0: (Our) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x300265e)
  * frame #0: 0x00000001a513ebec libobjc.A.dylib`invocation function for block in flushCaches(objc_class*, char const*, bool (objc_class*) block_pointer) + 60
    frame #1: 0x00000001a513e990 libobjc.A.dylib`flushCaches(objc_class*, char const*, bool (objc_class*) block_pointer) + 164
    frame #2: 0x00000001a5121c84 libobjc.A.dylib`attachCategories(objc_class*, locstamped_category_t const*, unsigned int, int) + 944
    frame #3: 0x00000001a51410dc libobjc.A.dylib`load_categories_nolock(header_info*)::$_9::operator()(category_t* const*) const + 352
    frame #4: 0x00000001a5140eec libobjc.A.dylib`load_categories_nolock(header_info*) + 84
    frame #5: 0x00000001a511e584 libobjc.A.dylib`map_images_nolock + 5548
    frame #6: 0x00000001a511cf4c libobjc.A.dylib`map_images + 100
    frame #7: 0x00000001007530c0 dyld`dyld4::RuntimeState::notifyLoad(dyld3::Array<dyld4::Loader const*> const&) + 624
    frame #8: 0x0000000100769c54 dyld`dyld4::APIs::dlopen_from(char const*, int, void*) + 504
    frame #9: 0x00000001ae000ee8 HIToolbox`InitShortcut + 76
    frame #10: 0x00000001ae14fa9c HIToolbox`SetupMenuTracking(MenuSelectData&, unsigned char, Point, double, MenuData*, unsigned int, unsigned short, Rect const*, Rect const*, __CFDictionary const*, unsigned int, unsigned int, Rect const*, __CFString const*) + 224
    frame #11: 0x00000001ae01dfcc HIToolbox`MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*) + 212
    frame #12: 0x00000001ae01de48 HIToolbox`_HandleMenuSelection2 + 416
    frame #13: 0x00000001a806b6f0 AppKit`_NSHandleCarbonMenuEvent + 300
    frame #14: 0x00000001a806b4d0 AppKit`_DPSEventHandledByCarbon + 68
    frame #15: 0x00000001a7ecd5b4 AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 3280
    frame #16: 0x00000001a7ebefe0 AppKit`-[NSApplication run] + 596
    frame #17: 0x00000001a7e906fc AppKit`NSApplicationMain + 1132
    frame #18: 0x000000010074908c dyld`start + 520

Debugger breaking at dlopen_from:

$ lldb Our.app/Contents/MacOS/Our
(lldb) breakpoint set --name dlopen_from
(lldb) r
# App starting, all is OK
# Click on main menu
Process 37360 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
    frame #0: 0x00000001a52a4d7c libdyld.dylib`dlopen_from
libdyld.dylib`dlopen_from:
->  0x1a52a4d7c <+0>:  mov    x3, x2
    0x1a52a4d80 <+4>:  mov    x2, x1
    0x1a52a4d84 <+8>:  mov    x1, x0
    0x1a52a4d88 <+12>: adrp   x8, 364400
Target 0: (Our) stopped.
(lldb) register read
General Purpose Registers:
        x0 = 0x00000001a5794fb1  "/System/Library/PrivateFrameworks/RunningBoardServices.framework/RunningBoardServices"
        x1 = 0x0000000000000001
        x2 = 0x00000001a54b7454  CoreFoundation`RunningBoardServicesLibrary + 164
        x3 = 0x0000000000000000
        x4 = 0x0000000000000000
        x5 = 0x0000000000000000
        x6 = 0x0000000000000000
        x7 = 0x0000000000000000
        x8 = 0xd4f0021d983e007e
        x9 = 0x00000001a5794fb1  "/System/Library/PrivateFrameworks/RunningBoardServices.framework/RunningBoardServices"
       x10 = 0x00000001a5556000  CoreFoundation`_NSSharedKeySetS_keys + 30014
       x11 = 0x00000000054e0820
       x12 = 0x0000000000000002
       x13 = 0x00000000003dc000
       x14 = 0x0000000000002604
       x15 = 0x00000000000000a7
       x16 = 0x00000001a52a4d7c  libdyld.dylib`dlopen_from
       x17 = 0x00000001ffaf1240  (void *)0x00000001a52a4d7c: dlopen_from
       x18 = 0x0000000000000000
       x19 = 0x000000016fdfc290
       x20 = 0x00000001a54b7454  CoreFoundation`RunningBoardServicesLibrary + 164
       x21 = 0x000000016fdfc260
       x22 = 0x000000016fdfc290
       x23 = 0x00000001aa690e18  SkyLight`SLSDisplayStatusQuery
       x24 = 0x43258001a54b7454 (0x00000001a54b7454) CoreFoundation`RunningBoardServicesLibrary + 164
       x25 = 0x00000001aa6919e4  SkyLight`SLSMainDisplayID
       x26 = 0x00000001aa6a5494  SkyLight`SLSRegisterConnectionNotifyProc
       x27 = 0x00000001aa510c24  SkyLight`SLSPackagesEnableConnectionWindowModificationNotifications
       x28 = 0x00000000000e02b7
        fp = 0x000000016fdfc240
        lr = 0x00000001afe0bc50  SoftLinking`_sl_dlopen + 100
        sp = 0x000000016fdfc1c0
        pc = 0x00000001a52a4d7c  libdyld.dylib`dlopen_from
      cpsr = 0x20001000

If we don't enable Sparkle (i.e. don't call PBAppAutoUpdateEnable()), no crash. We have been running this app for months (without Sparkle) without ever seeing this crash.

Possible Fix

Ideas:

Version

Sparkle 2.6.0, pre-built from https://github.com/sparkle-project/Sparkle/releases/tag/2.6.0 Tested on macOS 12.6.3 on M1/arm64 hardware. Built with clang/llvm 15.0.7

rsms commented 5 months ago

I tried building Sparkle from source, using Xcode, but I can't get past this error, which appears immediately after opening the Sparkle Xcode project after unzipping the source archive from https://github.com/sparkle-project/Sparkle/releases/tag/2.6.0

Screen Shot 2024-04-26 at 10 28 58
rsms commented 5 months ago

~We tried using Sparkle 2.5.2 and Sparkle 2.4.2 and both of those works! So this seems like a regression in 2.6.0.~

Nope, still crashes with Sparkle.framework from both 2.4.2 and 2.5.2. However, strangely, it does not crash every time with 2.4.2 or 2.5.2.