6

AppKit State Restoration on macOS 14 Sonoma

 8 months ago
source link: https://milen.me/writings/appkit-state-restoration-macos-14-sonoma/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

milen.me — AppKit State Restoration on macOS 14 Sonoma

AppKit State Restoration on macOS 14 Sonoma

Published on 20 Dec, 2023

AppKit State Restoration on macOS 14 Sonoma

AppKit state restoration behaviour changed on macOS 14 Sonoma in a subtle way that can lead to apps not restoring their state correctly. The change can lead to silent breakages which can be hard to debug.

Behavioural Changes

The AppKit release notes for macOS 14 state:

Secure coding is automatically enabled for restorable state for applications linked on the macOS 14.0 SDK. Applications that target prior versions of macOS should implement NSApplicationDelegate.applicationSupportsSecureRestorableState() to return true so it’s enabled on all supported OS versions.

As usual, the behavioural changes only apply to apps that have been linked against the latest SDK to preserve backwards compatibility for existing apps.

Consequences

The relase notes don’t make it immediately clear what the consequences of automatically enabling secure coding might be. In practice, it means that secure coding violations can now occur. The response to such violations depends on the value of NSCoder’s decodingFailurePolicy property which can be one of:

  • NSDecodingFailurePolicyRaiseException: A failure policy that directs the coder to raise an exception.
  • NSDecodingFailurePolicySetErrorAndReturn: A failure policy that directs the coder to capture the failure as an error object.

The decodingFailurePolicy property is readonly, thus outside of our control as the NSCoder object is created by the framework. AppKit uses NSDecodingFailurePolicySetErrorAndReturn as the default policy for state restoration.

The documentation states:

On decode failure, the NSCoder will capture the failure as an NSError, and prevent further decodes (by returning 0 / nil equivalent as appropriate).

Thus, after a secure coding violation, subsequent decoding operations would silently fail.

Secure Coding Violations

NSSecureCoding docs demonstrate the canonical secure coding violation:

Historically, many classes decoded instances of themselves like this:

id obj = [decoder decodeObjectForKey:@"myKey"];
if (![obj isKindOfClass:[MyClass class]]) { /* ...fail... */ }

This technique is potentially unsafe because by the time you can verify the class type, the object has already been constructed, and if this is part of a collection class, potentially inserted into an object graph.

So any usages of -[NSCoder decodeObjectForKey:] would trigger a secure coding violation. The docs for NSCoder.decodingFailurePolicy provide further examples:

A decode call can fail for the following reasons:

  • …snip…

  • A secure coding violation occurs. This happens when you attempt to decode an object that doesn’t conform to NSSecureCoding. This also happens when the encoded type doesn’t match any of the types passed to decodeObject(of:forKey:).

How to Debug

Violations can now arise in any -restoreStateWithCoder: implementations, so they need to be audited.

  • Check for any usages of -[NSCoder decodeObjectForKey:].
    • Replace with the appropriate secure variants.
  • At the end of -restoreStateWithCoder:, check the value of NSCoder.error property.
    • If it’s non-nil, an error must have occurred earlier.

References

← Back to Writings

Copyright © Milen Dzhumerov
Email

RSS

MastodonTwitter


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK