Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Last Week on My Mac: Death, taxes and macOS updates

By: hoakley
2 March 2025 at 16:00

‘Tis impossible to be sure of any thing but Death, Taxes and macOS updates.
(Modified with apology from the original, said by Toby Guzzle in Christopher Bullock’s play The Cobbler of Preston (1716), quoted in turn by Daniel Defoe and most famously by Benjamin Franklin in 1789.)

Last week my iMac Pro was updated against my wishes from macOS Sequoia 15.1.1 to 15.3.1. Although it wasn’t my intention, it proved a relief in two ways, first that my ageing iMac Pro survived the process without losing any data or dying completely, and second that I had at last caught a forced update red-handed. For some years I have been aware of many who suffered a similar fate, where they had been careful to avoid upgrading or updating macOS, but had eventually succumbed to it unwittingly. At last I was able to experience this at first hand, and capture log excerpts to discover just what happened.

Deceit

My conclusions were:

  • Software update notifications tricked me into unwittingly agreeing to perform a macOS update.
  • That update was expressly against my Software Update settings.
  • I was given no second chance to confirm I intended the update to take place.
  • The update was scheduled to be performed when my Mac was unattended.
  • DAS scheduling and dispatch were unaware of the scheduled backups to be performed later that night, and dispatched the update at a time before those backups were scheduled. Had anything gone wrong in the update, I could have had to fall back on backups made nearly 24 hours earlier, and would have lost a whole day’s changes.

What I’d like to see is a change to the process initiated by opting to perform a delayed update, either later or that night. If the user opts for that, then Software Update should display a clear confirmation dialog, offering options to cancel the update or postpone it further. If the user does accept, then they should be offered a timeframe for the update to be performed, to allow it to be scheduled after any nightly backups.

Above all, the user should never be given a forced choice between updating now or later tonight, and there should always be a third option to defer further.

This has been a long-running flaw in the behaviour of macOS that has shocked and antagonised many users over several years. Although we’re all in favour of Apple encouraging and facilitating us to keep macOS up to date, there’s neither need nor excuse to do so by deceiving us by trickery. Deceit undermines confidence in both Apple and its products and is notoriously bad marketing and support.

This chart shows how I believe the process works, from the initial notification options to starting the update.

Opacity and persistence

During my investigation of how this unwanted update had occurred, I hadn’t expected to meet my old friend Duet Activity Scheduler (DAS). As I traced through the log extracts it became clear that, once the update had been scheduled by DAS, the only way to postpone or abort it would have been to shut the Mac down. Activities scheduled by DAS-CTS are hidden from the user, who has neither awareness nor control over them.

DAS and its linked XPC Activity subsystem, alias Centralised Task Scheduling or CTS, now manage over 500 background activities in macOS, including Time Machine backups and XProtect Remediator scans. They’re one of the few parts of the system that remains almost inaccessible. DAS manages lists of activities that can’t be inspected, and dispatches them according to opaque criteria. Once an activity is scheduled by DAS, there’s no way a user can remove it from its lists, so it will inexorably attain a score sufficient to pass that set by DAS as its threshold. For a few brief moments that activity will be visible among running processes, then vanish again into obscurity.

If I wanted to design persistent code that periodically harvests and send sensitive data to a remote server, DAS-CTS would be highly attractive. As there’s no way to inspect its scheduled activities, no security software could discover the existence of that activity, unless they were fortunate enough to catch it while it’s running briefly. Such activities don’t need a tell-tale LaunchDaemon or LaunchAgent, but can be arbitrary code in a completion handler within an apparently innocent app. They’re run using XPC, but without its formalities or restrictions.

DAS-CTS seems to rely largely on security through obscurity, and opening up inspection of its activity lists could be a valuable first step in preventing its abuse. It has enjoyed a decade since its release in 2014 apparently without being exploited, although its opacity makes it difficult to know that with any confidence. Perhaps it’s time for a reassessment.

How your Mac can update macOS when you don’t want it to

By: hoakley
27 February 2025 at 15:30

Over the last few years, many have reported that their Macs spontaneously updated or even upgraded macOS when they didn’t expect them to, and often against their wishes. This can occur when Software Update in System Settings has Install macOS updates turned off. Explanations of how Apple appears to be able to override that setting have so far been lacking; this article explains how it happened overnight to my iMac Pro, when it updated itself from Sequoia 15.1.1 to 15.3.1.

Spontaneous macOS update

My story will be familiar to others who have suffered a similar forced update: I came down in the morning to discover my iMac Pro had shut down when I had left it running just seven hours earlier. A little detective work in its log revealed what had happened while I had been asleep.

Although I have kept this iMac Pro up to date with macOS since I first installed Mojave on it on 18 November 2018, it’s growing old and a bit creaky, and the update to Sequoia 15.1.1 on 19 November last year was slightly traumatic, with a series of shutdowns rather than restarts. I therefore decided to leave it running 15.1.1 until I had completed migrating to my Mac mini M4 Pro this Spring.

As a result, it had periodically notified me of updates to 15.2, 15.3, and most recently 15.3.1, each of which I had politely declined. Those notifications became more persistent, and one or two gave me either of two options, to update now, or later that night, and couldn’t otherwise be dismissed. I therefore chose to defer the update until the night, and nothing came of them.

One of those notifications, though, decided to end my procrastination and added a background activity named com.apple.SUOSUScheduler.tonight.install to the DAS-CTS scheduling system. In the small hours of the morning, DAS rescored its list of activities, and decided that it was time to dispatch that task, writing these entries in the log:
01:39:07.463 com.apple.duetactivityscheduler Rescoring all 499 activities [Entered SmartPowerNap]
01:39:07.490 com.apple.duetactivityscheduler 0:com.apple.SUOSUScheduler.tonight.install:3370CB:[{name: Thermal Policy, policyWeight: 5.000, response: {0, 0.20, }}, Decision: CP Score: 0.915843}
01:39:07.490 com.apple.duetactivityscheduler '0:com.apple.SUOSUScheduler.tonight.install:3370CB' CurrentScore: 0.915843, ThresholdScore: 0.162610 DecisionToRun:1
01:39:07.589 com.apple.xpc.activity Initiating: com.apple.SUOSUScheduler.tonight.install (0x7fd1d582a720)

That activated SoftwareUpdate, which ignored my user settings and proceeded to install the update:
01:39:07.589 com.apple.SoftwareUpdate SUOSUTonightObserver: Tonight activity fired!
01:39:07.590 com.apple.powerd Process softwareupdated.308 Created MaintenanceWake "com.apple.SoftwareUpdate.TonightActivityTrigger" age:00:00:00 id:55834610938 [System: SRPrevSleep kCPU]
01:39:07.590 com.apple.SoftwareUpdate SUOSUTonightObserver: Proceeding with updates
01:39:07.590 com.apple.SoftwareUpdate SUOSUServiceDaemon: Chose on-console client: SoftwareUpdateNotificationManager (type = sunm, pid = 698, uid = 501, path = /System/Library/PrivateFrameworks/SoftwareUpdate.framework/Versions/A/Resources/SoftwareUpdateNotificationManager.app/Contents/MacOS/SoftwareUpdateNotificationManager)

It turns out that these ‘DoItLater’ or ‘installTonight’ updates are already well provided for by SoftwareUpdate:
01:39:08.243 com.apple.SoftwareUpdate Successful call to startInstallingDoItLaterUpdates
01:39:08.244 com.apple.SoftwareUpdate SUOSUShimController: Start installing updates: {(<SUOSUProduct: MSU_UPDATE_24D70_patch_15.3.1_minor>)}, options: { DoInForeground = 0; DoItLater = 1; ForceRestart = 0; InitiatingClient = 2; MDMInitiated = 0;}
01:39:08.245 com.apple.SoftwareUpdate SUOSUMobileSoftwareUpdateController: Updating additionalUpdateMetricEventFields: {autoUpdate = false; buddy = false; commandLine = false; ddm = false; installTonight = true; mdm = false; notification = true; settings = false; }

The update process carefully avoided revealing what was about to happen:
01:39:08.362 com.apple.SoftwareUpdate MSU update already prepared, skip showing license agreement
01:39:08.396 com.apple.SoftwareUpdate Skipping showing the SLA
01:39:08.396 com.apple.SoftwareUpdate SUOSUShimController: Updates require post-install action (restart), installing now
01:39:08.396 com.apple.SoftwareUpdate SUOSUShimController: MSU update already prepared
01:39:08.396 com.apple.SoftwareUpdate SUOSUShimController: Download required: 0 (legacy=0, MSU=0)
01:39:08.396 com.apple.SoftwareUpdate SUOSUShimController: Notification manager client, proceeding with countdown notification flow without confirmation
01:39:08.397 com.apple.SoftwareUpdate SUOSUNotificationUpdateService: Install did begin for updates: ( "<SUOSUProduct: MSU_UPDATE_24D70_patch_15.3.1_minor>")

Local authentication was also disabled to ensure that nothing stood in the way of the imminent update:
01:39:08.443 com.apple.SoftwareUpdate SUAppStoreUpdateController: authorize
01:39:08.508 com.apple.SoftwareUpdate SUOSUAuthenticationManager: Disabling local authentication requirement
01:39:08.556 com.apple.SoftwareUpdate SUOSUMobileSoftwareUpdateController: Download finished: (null)
01:39:08.557 com.apple.SoftwareUpdate SUOSURestartCountdownOperation: Successful downloads, proceed to countdown (downloadOnly=0)
01:39:08.561 com.apple.SoftwareUpdate SUOSURestartCountdownOperation: Starting restart countdown
01:39:09.713 com.apple.SoftwareUpdate SUOSUCountdownNotification: seconds remaining: 60
01:40:08.570 com.apple.SoftwareUpdate SUOSUCountdownNotification: seconds remaining: 1
01:40:08.573 com.apple.SoftwareUpdate SUOSUUpdateController: Sending authorization to softwareupdated
01:40:08.598 com.apple.SoftwareUpdate SUOSUUpdateController: Successfully authorized with softwareupdated
01:40:08.953 com.apple.SoftwareUpdate Restarting for software update (forced=0)
01:40:10.958 com.apple.SoftwareUpdate Starting post-logout mode (skipConfirm = 1, reconnectMode = 0, shouldShutdown = 0)
01:40:10.958 com.apple.SoftwareUpdate SUOSUAuthenticationManager: Disabling local authentication requirement
01:40:11.523 com.apple.SoftwareUpdate SUOSUAuthorizationController: Non-interactive authorization succeeded for non-admin user
01:40:11.523 com.apple.SoftwareUpdate SUOSUUpdateController: Sending authorization to softwareupdated
01:40:11.562 com.apple.SoftwareUpdate SUOSUUpdateController: Successfully authorized with softwareupdated
01:40:42.389 com.apple.SoftwareUpdate SUHelper: Rebooting (success = 1, night install = 1, shutdown = 1)
01:40:42.396 com.apple.SoftwareUpdate SUHelper: Preparing for night install

What happened next was unexpected by macOS, though:
01:40:46.875 === system wallclock time adjusted
and that was the last entry in the log until the initial kernel boot entry over five hours later when I started the Mac up:
06:57:24.842 === system boot: 78F481CC-E26F-464C-BEB7-6E26E49DB8DC

At 03:15 and 04:15 that morning, full backups should have been made of the Data and another working volume. Because at that time the Mac was shut down in the middle of a possibly failed update, those backups were never made. Thankfully when it started up just before 07:00 it was able to complete the update and then resumed normal service.

Points of note

  • Software update notifications tricked the user into unwittingly agreeing to perform a macOS update.
  • That update was expressly against Software Update settings.
  • The user was given no second chance to confirm they intended the update to take place.
  • The update was scheduled to be performed when the Mac was unattended.
  • DAS scheduling and dispatch were unaware of the scheduled backups to be performed later that night, and dispatched the update at a time before those backups were scheduled.
  • As the update was scheduled to be performed unattended, no warning was given when it was about to start, and there was no opportunity to delay the update until after backups had been completed.
  • Once the update had been scheduled by DAS, the only way to postpone or abort it would have been to shut the Mac down. Activities scheduled by DAS-CTS are hidden from the user, who has neither awareness nor control over them.
  • The overall effect is that macOS enforces updates on the user against their express settings, without giving them the opportunity to postpone or abort the update.

❌
❌