Skip to content

ios: run qemu-img in a helper extension on iOS 26 - #7908

Merged
osy merged 2 commits into
mainfrom
feature/ios-plugin
Sep 24, 2026
Merged

osy merged 2 commits into
mainfrom
feature/ios-plugin

Conversation

@osy

@osy osy commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

QEMU tools cannot run in the app process on iOS: a process may load QEMU only once and qemu-img calls exit(). So disk snapshots of a stopped VM, Reclaim Space, Compress, Resize and qcow2 conversion on drive import were macOS-only.

iOS 26 brings ExtensionKit, so qemu-img now runs in an ExtensionKit extension embedded in the app (iOSHelper, and iOSHelper-SE for UTM SE since an extension's bundle identifier must be prefixed by its host's). The helper speaks the same QEMUHelperProtocol as the macOS XPC helper but loads the tool into its own process, one tool per process; the app launches a fresh helper for each operation and tears it down with a handshake on the system's interruption callback, because a launch that comes too soon otherwise gets a process that is still exiting. Files are passed as bookmarks, whose implicit security scope grants the resolving process access, so the file path is unchanged from macOS.

The shared UTMQemuImage code also gets three fixes that surfaced while testing: it reads the tool's output to end-of-file instead of racing the exit message, resumes its continuations off the XPC reply queue (NSXPC makes a call issued from that queue synchronous, which deadlocked on macOS), and stops the process explicitly since the connection's exported object kept it from ever being deallocated. The last stderr line becomes the error text when the tool gives none. The Resize confirmation moved into the resize popover, as an alert requested while the sheet closes is dropped on iPhone.

A second commit adds the helper schemes Xcode created, drops the incidental Address Sanitizer setting from the UTM SE scheme (a sanitized extension does not start on a device), and takes the Metal API validation setting Xcode 27 writes into the macOS scheme.

Release changes: signed iOS exports need a provisioning profile for the helper (package.sh takes it as a new argument, the App Store job reads IOS_SE_HELPER_PROFILE_DATA/IOS_SE_HELPER_PROFILE_UUID), documented in Release.md, iOSDevelopment.md and CodeSigning.xcconfig.sample.

Resolves #4774

Testing: Tested by a human on iOS 26.7, iPhone 16 Pro (UTM SE). The author acknowledges that this change has been tested and/or reviewed by a human in accordance with UTM's AI contribution guidelines.

Additional automated checks: iOS 26.1 and 27.0 simulators (snapshots, reclaim, resize through the UI), iOS 17.0 simulator (features hidden, app installs with the extension embedded), macOS 27 host and a macOS 12.6 guest (qemu-img through QEMUHelper, snapshots and resize through the UI).

@osy osy added this to the v5.0 milestone Sep 23, 2026
osy added 2 commits September 23, 2026 23:10
QEMU tools cannot run in the app process on iOS: a process may load QEMU
only once and qemu-img calls exit(). So disk snapshots of a stopped VM,
reclaiming space, compression, resizing and conversion on import were
macOS-only.

iOS 26 brings ExtensionKit, so qemu-img now runs in an ExtensionKit
extension (iOSHelper, and iOSHelper-SE for UTM SE since an extension's
bundle identifier must be prefixed by its host's). The helper speaks the
same QEMUHelperProtocol as the macOS XPC helper but loads the tool into
its own process, one tool per process. UTMProcess accepts an injected
connection so the remote path is shared with macOS, including file
access through bookmarks, whose implicit security scope grants the
resolving process access. UTMHelperProcess launches the helper and,
because the system hands a process that is still exiting to a launch
that comes too soon, tears it down with a handshake on the interruption
callback and runs helpers one at a time.

UTMQemuImage now reads the tool's output until it ends instead of
racing the exit message, resumes its continuations off the XPC reply
queue (a call made from that queue blocks in NSXPC), and stops the
process explicitly since the connection's exported object kept it from
ever being deallocated on macOS. The last stderr line becomes the error
text when the tool gives none.

Assisted-by: Claude:claude-fable-5-1
The iOS-SE scheme has run with Address Sanitizer since the schemes were
first committed to set the architecture override, which was incidental.
An ExtensionKit extension linked with the sanitizer does not start on a
device, so every qemu-img operation of UTM SE failed when run from
Xcode. Turn it off, add the schemes Xcode created for the helper targets
so both can be built and debugged on their own, and take the Metal API
validation setting Xcode 27 writes into the macOS scheme.

Assisted-by: Claude:claude-fable-5-1
@osy
osy force-pushed the feature/ios-plugin branch from 774540b to a68e274 Compare September 24, 2026 04:23
@osy
osy merged commit 0c8d40e into main Sep 24, 2026
54 checks passed
@osy
osy deleted the feature/ios-plugin branch September 24, 2026 05:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reclaim Space on iOS?

1 participant