2 Punkte von GN⁺ 2025-04-06 | 1 Kommentare | Auf WhatsApp teilen
  • Apples Darwin ist die Unix-artige Grundlage von macOS, iOS und modernen Apple-Betriebssystemen; XNU ist ein hybrider Kernel, der Mach und BSD in einem Kernel kombiniert
  • XNU behält Machs Tasks, Threads, virtuellen Speicher und portbasierte IPC bei, platziert BSD-Dienste jedoch im selben Kernel-Adressraum und reduziert so die Kosten einer rein mikro-kernelartigen Nachrichtenübermittlung
  • Die Abstammungslinie von NeXTSTEP mit Mach 2.5 + 4.3BSD führte nach Apples Übernahme von NeXT im Jahr 1996 zu Mac OS X und Darwin; später kamen schrittweise FreeBSD-Code, I/O Kit, 64-Bit, ARM und Unterstützung für Apple Silicon hinzu
  • macOS und iOS haben Funktionen wie Sandboxing, Code-Signing, SIP, APFS, DriverKit, QoS-Scheduling, Jetsam und komprimierten Speicher durch Zusammenarbeit von Kernel und User Space erweitert
  • Die Entwicklung von XNU entspricht weniger einem Neuschreiben des Kernels, sondern eher dem Beibehalten der Mach/BSD-Basis, wobei leistungsrelevante Teile in den Kernel integriert und isolationsbedürftige Teile über IPC und User Space getrennt werden

Ausgangspunkt von Darwin und XNU

  • Darwin ist das Unix-artige Kernbetriebssystem, das macOS, iOS und Apples moderne OS-Plattformen trägt
  • Im Zentrum steht der XNU-Kernel, kurz für „X is Not Unix“, der einen Mach-Mikrokernel-Kern mit BSD-Unix-Komponenten verbindet
  • Diese Architektur nutzt sowohl Machs auf Nachrichtenübermittlung basierendes Design als auch BSDs Stabilität und POSIX-Kompatibilität und zielt auf ein Gleichgewicht zwischen Modularität und Performance

Geschichte von Mach über NeXTSTEP bis Mac OS X

  • Mach begann 1985 als Projekt an der Carnegie Mellon University unter der Leitung von Richard Rashid und Avie Tevanian
    • Es war ein Mikrokernel-Design, das nur Low-Level-Funktionen wie Speicherverwaltung, CPU-Scheduling und IPC im Kernel belassen und Dateisysteme, Networking und Treiber als User-Space-Server auslagern wollte
    • Konzepte wie Tasks, Threads, Mach-Ports, Copy-on-Write und Speicherobjekte wurden zu zentralen Kernel-Objekten
  • NeXTSTEP wurde 1989 auf Basis des Mach-2.5-Kernels mit einem darübergelegten 4.3BSD-Unix-Subsystem veröffentlicht
    • NeXT entschied sich zugunsten der Performance dafür, BSD-Code in den Kernel-Adressraum zu integrieren, statt einen reinen Mikrokernel-Ansatz zu verfolgen
    • Ebenfalls enthalten war das Objective-C-basierte DriverKit, das später in Apples XNU-Linie weitergeführt wurde
  • Apple übernahm NeXT 1996 und wählte NeXTSTEP als Grundlage für das neue Mac OS X
    • Mit dem Rhapsody-Projekt gelangte NeXTs hybrider Mach/BSD-Kernel zu Apple
    • Später übernahm XNU Code aus der Mach-3.0-Linie auf Basis von OSFMK 7.3 sowie 4.4BSD- und FreeBSD-Code

Frühe Entwicklung von Darwin und Mac OS X

  • Apple veröffentlichte 1999 die Entwickler-Preview von Mac OS X und machte 2000 mit Darwin 1.0 den XNU-Kernel und den grundlegenden Unix-User-Space für Entwickler zugänglich
  • Mac OS X 10.0 Cheetah erschien 2001 kommerziell auf Basis von Darwin 1.3.1
  • Der Schwerpunkt der frühen Änderungen lag darauf, die BSD-Schicht, Networking, Dateisysteme und Threading-Performance zu stärken
    • Mac OS X 10.1 Puma verbesserte die Performance der Thread-Verwaltung und die Unterstützung für Echtzeit-Threads
    • Mac OS X 10.2 Jaguar enthielt IPv6, IPSec, mDNSResponder und Journaling für HFS+
    • Mac OS X 10.3 Panther integrierte Kernel-Verbesserungen aus FreeBSD 5 und feingranulare Kernel-Locks, um Multiprozessor-Systeme besser zu nutzen
  • Mac OS X 10.4 Tiger erhielt die UNIX-03-Zertifizierung, führte FreeBSDs kqueue/kevent ein und behielt eine Cross-Platform-Grundlage für den Übergang zu Intel-Macs bei

64 Bit und die mobilen Anforderungen von iPhone OS

  • Mac OS X 10.5 Leopard führte auf Basis von Darwin 9 die Ausführung eines 64-Bit-Kernels, 64-Bit-Treiber, ASLR, Sandboxing und DTrace ein
  • 2007 erschien auch das erste iPhone OS auf Basis von Darwin 9, wodurch XNU auf mobile ARM-Geräte erweitert wurde
    • Da das frühe iPhone nur begrenzten RAM hatte und keinen Swap nutzen konnte, verwendete es den Jetsam-Mechanismus, der bei niedrigem Speicher Hintergrund-Apps beendet
    • iPhone OS führte Drittanbieter-Apps in einer Sandbox aus und verlangte striktes Code-Signing für Binärdateien
  • Mac OS X 10.6 Snow Leopard stellte die Unterstützung für PowerPC ein und verstärkte die auf Intel ausgerichtete Optimierung für 64 Bit und Mehrkernsysteme
    • Grand Central Dispatch und libdispatch sind User-Space-Bibliotheken, nutzen aber Thread-Pools und Scheduling-Unterstützung des Kernels
    • Auch OpenCL erforderte für GPU-Computing eine enge Integration von User-Space-Frameworks und Kernel-Treibern
  • iOS 4 passte den Scheduler an Prioritätsunterscheidungen für Hintergrund-Apps und die Unterstützung von Mehrkern-ARM-SoCs an

Erweiterung moderner Kernel-Funktionen in macOS und iOS

  • OS X 10.9 Mavericks fügte komprimierten Speicher und Timer Coalescing hinzu
    • Komprimierter Speicher komprimiert inaktive Pages im RAM und reduziert so Disk-Swapping
    • Timer Coalescing senkt den Stromverbrauch, indem CPU-Wakeup-Zeitpunkte zusammengelegt werden
  • OS X 10.11 El Capitan führte System Integrity Protection, kurz SIP, ein
    • SIP wird vom Kernel über das Mandatory-Access-Control-Framework der BSD-Schicht erzwungen und verhindert, dass selbst Root-Prozesse wichtige Systemdateien und Prozesse verändern
  • macOS 10.13 High Sierra führte APFS als Standarddateisystem ein
    • Die VFS-Schicht von XNU wurde erweitert, um APFS-Snapshots, Cloning und Verschlüsselung auf Container-Ebene zu unterstützen
    • Zur selben Zeit wurde für das Laden von Drittanbieter-kexts eine Benutzerbestätigung erforderlich
  • macOS 10.15 Catalina führte das moderne DriverKit ein
    • DriverKit verlagert viele Treiber aus dem Kernel heraus in Driver Extensions im User Space
    • Der Kernel gewährt User-Space-Treibern über IPC und Shared Memory begrenzten Zugriff auf Hardware
    • Catalina führte außerdem ein schreibgeschütztes System-Volume ein und verstärkte so den SIP-Schutz

XNU in der Ära von Apple Silicon

  • macOS 11 Big Sur und Darwin 20 waren 2020 die ersten Releases mit Unterstützung für Apple-Silicon-Macs
  • XNU unterstützte über iOS bereits ARM, musste bei Apple-Silicon-Macs aber auch eine heterogene big.LITTLE-CPU-Architektur berücksichtigen
    • Der Scheduler erkennt heterogene Kerne, sodass Threads mit hoher Priorität und hoher Last auf Performance-Kernen und Threads mit niedriger QoS oder Hintergrund-Threads auf Effizienz-Kernen platziert werden können
    • QoS-Klassen dienen auf Apple Silicon als Scheduling-Hinweise, die auch die Auswahl des Kerntyps beeinflussen können
  • In der Unified-Memory-Architektur von Apple Silicon verwalten der Kernel-Speichermanager und GPU-Treiber das Teilen von Puffern
    • Die Mach-VM-Abstraktion eignet sich dafür, Speicherobjekte zwischen User Space und GPU nicht per Kopie, sondern per VM-Remapping zu teilen
  • Das ARM64-Backend unterstützt Pointer Authentication, nutzt PAC-Schlüssel für Exception Frames und System-Pointer und trägt zur Abschwächung von ROP-Angriffen bei
  • XNU bleibt die gemeinsame Grundlage mehrerer Apple-Plattformen wie macOS, iOS, watchOS, tvOS, bridgeOS und visionOS

Die hybride Kernel-Architektur von XNU

  • Die Mach- und BSD-Komponenten von XNU werden zu einem einzigen Kernel-Binary gelinkt und teilen denselben Adressraum
    • Zwischen Mach und BSD gibt es keine Schutzgrenze; innerhalb des Kernels interagieren sie nicht über IPC-Nachrichten, sondern über normale Funktionsaufrufe
    • Unix-Systemaufrufe wie read() senden keine Nachricht an einen separaten BSD-Server, sondern gehen direkt in den BSD-Dateisystemcode im Kernel
  • Mach übernimmt die zentrale Kernel-Infrastruktur
    • Es verwaltet Erstellung und Beendigung von Tasks und Threads, Context Switching, Low-Level-Scheduling, Locks, Timer und Scheduling-Queues
    • Jeder BSD-Prozess entspricht einem Mach-Task, jeder Thread einem Mach-Thread
    • Mach VM stellt virtuelle Adress-Maps, Speicherobjekte, Copy-on-Write und IPC-basiertes Teilen von Speicher bereit
  • BSD liefert den Unix-Charakter und die Dienste
    • Es verwaltet PIDs, Benutzer-IDs, Signale, POSIX-Threads, Dateisysteme, Networking, Unix-IPC, Geräte-I/O, Berechtigungen und Security-Frameworks
    • VFS behandelt Dateisysteme wie HFS+, APFS und NFS und ist bei memory-mapped Files über Mach VM und den vnode pager angebunden
    • Sandboxing und SIP funktionieren durch die Zusammenarbeit von BSD-Sicherheitsmodulen und Einschränkungen für Mach-Task-Ports
  • I/O Kit ist die dritte Säule von XNU: ein objektorientiertes Treiber-Framework, geschrieben in einer eingeschränkten Form von C++
    • Geräte und Treiber werden als Klassenhierarchie dargestellt, Treiber laufen im Kernel als C++-Objekte
    • Dem User Space wird über Eigenschaften der I/O Registry und User-Client-Interfaces begrenzter Zugriff gewährt
    • Bis zum DriverKit des modernen macOS liefen die meisten Treiber im Kernel als kexts

Mach IPC und Systemdienste

  • XNU verwendet für den Pfad von Unix-Systemaufrufen keine Mach-Nachrichten, nutzt Mach IPC aber umfassend für User-Space-Dienste sowie die Kommunikation zwischen Kernel und Prozessen
  • Mach-Ports dienen als User-Space-Handles für verschiedene Kernel-Objekte
    • Jeder Task hat einen Task-Port; privilegierte Prozesse können darüber andere Tasks inspizieren oder steuern
  • Auch Ereignisse und Benachrichtigungen werden als Mach-Nachrichten übertragen
    • WindowServer erhält Benutzereingabe-Ereignisse vom Kernel als Mach-Nachrichten
    • Grand Central Dispatch nutzt intern Mach-Ports, um Threads, die auf Ereignisse warten, schlafen zu legen
    • kqueue/kevent kann gleichzeitig auf Mach-Port-Nachrichten und File Descriptors warten
  • Apples XPC-Framework ist auf Mach-Nachrichten aufgebaut
    • XPC-Verbindungen basieren intern auf Mach-Ports
    • Das Berechtigungsmodell von Mach-Ports wird in Diensten wie Keychains securityd zur Prüfung von Aufruferberechtigungen verwendet
    • Mach-Nachrichten können Out-of-Line-Speicher und Port-Rechte übertragen und werden daher für den Aufbau höherwertiger RPCs genutzt
  • MIG, der Mach Interface Generator, wird verwendet, um Interface-Definitionen und Code zum Senden und Empfangen von Nachrichten zwischen Kernel und User Space zu erzeugen

Scheduler und Thread-Verwaltung

  • Der XNU-Scheduler ging von Machs prioritätsbasiertem Round-Robin-Scheduler aus, wurde aber stark an Desktop- und Mobilanforderungen angepasst
  • Mach definierte historisch Thread-Prioritäten im Bereich 0 bis 127, und XNU verwendet Werte wie sched_pri und base_pri
    • Time-Sharing-Threads können ihre Priorität je nach Nutzung verändern
    • Echtzeit-Threads verwenden feste Prioritäten
  • XNU nutzt CPU-spezifische Run Queues und Scheduler-Interrupts für Effizienz und Load Balancing
  • Das App-Sandboxing und die Hintergrundausführung in iOS spiegeln Konzepte wie Aufgabenrollen oder Prioritätsgruppen im Scheduler wider
  • QoS-Klassen sind seit iOS 8 und OS X 10.10 in das Scheduling integriert
    • Klassen wie user-interactive, user-initiated, default, utility und background beeinflussen Prioritätsbereiche und Scheduling
    • Mit Grand Central Dispatch oder NSThread erstellte Threads erben QoS
    • Auf Apple Silicon können Hintergrund-QoS-Threads auf Effizienz-Kernen platziert werden
  • Für Echtzeit-Audio und wichtige Aufgaben werden auch Echtzeit-Queues und deadline-basiertes Scheduling unterstützt

Speicherverwaltung und Mach VM

  • Die Speicherverwaltung von XNU wird vom Mach-VM-Subsystem getragen
  • Jeder Mach-Task besitzt einen virtuellen Adressraum, der durch eine VM map und VM regions dargestellt wird
    • fork() kopiert nicht sofort den gesamten Speicher, sondern nutzt Copy-on-Write
    • Eltern- und Kindprozess teilen dieselben Pages, bis geschrieben wird
  • Mach verwendet Konzepte von Speicherobjekten und Pagern
    • Der Standard-Pager für anonymen Speicher wird vom User-Space-Daemon dynamic_pager übernommen und verwaltet bei Bedarf Swap-Dateien
    • Dateispeicher wird durch den vnode pager in der BSD-Schicht des Kernels abgewickelt, der mit dem Dateisystemcode interagiert
  • Mavericks’ komprimierter Speicher wurde durch einen compression pager im Kernel umgesetzt
    • Bei hohem Speicherdruck werden inaktive Pages nicht sofort auf die Disk geschrieben, sondern komprimiert in einem Compressor Pool im RAM gespeichert
    • Wenn die Komprimierung nicht ausreicht, wird Disk-Swap verwendet
  • Die Verwaltung des physischen Speichers übernimmt pmap, eine architekturabhängige Schicht
    • pmap verwaltet Page Tables oder entsprechende Strukturen der jeweiligen Architektur
    • Auf ARM64 sind auch Sicherheitsfunktionen und Cache-bezogene Fragen mit pmap verbunden
  • Der Shared Cache von dyld wird effizient genutzt, indem dieselben physischen Pages schreibgeschützt in mehrere Prozesse gemappt werden

Virtualisierungsunterstützung

  • Auf Intel-Macs stellt OS X seit 10.10 das Hypervisor.framework bereit, um User-Space-Virtualisierung zu unterstützen
    • Es nutzt Intel VT-x, damit ein User-Space-Prozess wie ein Virtual Machine Monitor agieren kann
    • Tools wie xhyve und einige Virtualisierungs-Apps nutzen diese Funktion
  • Auf Apple Silicon läuft das Virtualization.framework von macOS 11 auf einem kernelinternen Hypervisor für ARM64
    • Entwickler können im User Space Linux- oder macOS-VMs ausführen
    • Statt beliebige Drittanbieter-Hypervisoren im Kernel zuzulassen, erfolgt der Zugriff über Apple-Frameworks und Berechtigungen
  • Aus Kernel-Sicht umfassen Hypervisor-Funktionen die Verwaltung des physischen Gast-Speichers, Trap-and-Emulate für sensitive Instruktionen und das Bereitstellen von vCPU-Interfaces
  • Der Mach-Scheduler plant vCPUs, die aus Host-Sicht Threads sind, und das Speichersubsystem wird für Guest-Memory-Mappings verwendet
  • Auch unter iOS sind Virtualisierungsfunktionen unter bestimmten Bedingungen und Berechtigungen möglich; es gab Fälle, in denen auf jailbroken A14-Geräten ein Hypervisor aktiviert wurde, um Linux-VMs auszuführen

Secure Enclave und Exclaves

  • macOS nutzt zwei Isolationsmechanismen namens Secure Enclave und Exclaves, um sensible Aufgaben und Daten zu schützen
  • Secure Enclave ist ein dediziertes gehärtetes Subsystem, das in Apple-SoCs integriert ist
    • Es ist in iPhone, iPad, T2- oder Apple-Silicon-Macs und anderen Geräten vorhanden
    • Es führt ein eigenes mikrokernelbasiertes Betriebssystem aus und verwaltet sensible Informationen wie kryptografische Schlüssel und biometrische Daten
    • Ziel ist es, wichtige Daten zu isolieren, selbst wenn der Haupt-Anwendungsprozessor oder der Kernel kompromittiert wird
  • Exclaves sind eine neuere Sicherheitsarchitektur, die in macOS 14.4 und iOS 17 auftauchte
    • Statt sensible Aufgaben in derselben Berechtigungsdomäne wie den Haupt-XNU-Kernel zu legen, werden einige Kernressourcen in eine separate „externally located“-Domäne ausgelagert
    • Ressourcen wie Apple-ID-Dienste, Audiopuffer, Sensordaten und Komponenten zur Verwaltung von Anzeigeleuchten gehören dazu
    • Spezielle kexts und private Frameworks wie ExclaveKextClient.kext, ExclaveSEPManagerProxy.kext und ExclavesAudioKext.kext sind an der Verwaltung beteiligt
  • Diese Trennung bietet eine zusätzliche Verteidigungsschicht, indem sie Aufgaben innerhalb einer Exclave isoliert, selbst wenn der Hauptkernel kompromittiert wird

Langfristige Designrichtung

  • Darwin und XNU sind weder ein vollständiger Mikrokernel noch ein vollständig monolithischer Kernel, sondern ein Mischdesign
  • Der Mach-basierte Kern half dabei, sich an neue Architekturen und Systemfunktionen anzupassen, während die BSD-Schicht eine POSIX-kompatible Umgebung sowie Unix-Tools und -APIs bereitstellte
  • Apple bewältigte auf XNU-Basis den CPU-Wechsel von PowerPC zu Intel und ARM sowie neue Gerätekategorien wie iPhone, Apple Watch und Apple Vision Pro
  • Kernel-Änderungen erfolgen hauptsächlich auf drei Arten
    • Neue Funktionen werden auf dem bestehenden Kernel erweitert
    • Leistungsrelevante Komponenten werden im Kernel integriert
    • Komponenten, die Isolation benötigen, werden über Mach IPC und User Space getrennt
  • Die öffentlichen Source-Releases von Darwin bieten Forschern ein Fenster, um einen kommerziellen Hybrid-Kernel zu untersuchen, der öffentliche Umfang ist jedoch begrenzt

1 Kommentare

 
GN⁺ 2025-04-06
Meinungen auf Hacker News
  • Das virtuelle Speichersystem von Mach ist nicht nur in 4.4BSD und FreeBSD eingeflossen, sondern auch in NetBSD[0] und OpenBSD[1], offenbar aber nicht in DragonFly BSD[2]
    [0] https://netbsd.org/docs/kernel/uvm.html
    [1] https://man.openbsd.org/OpenBSD-3.0/uvm.9
    [2] https://www.dragonflybsd.org/mailarchive/kernel/2011-04/msg0...

    • Genau genommen stimmt das nicht. 386BSD, FreeBSD und NetBSD haben anfangs ein Design im Stil von Mach 2.5 geerbt, aber FreeBSD ersetzte recht schnell sämtliche verbliebenen Spuren der Mach-VM[0] durch eine moderne, leistungsfähige Neufassung der VM
      Zu Zeiten von FreeBSD 4 war im Kernel-Codebestand kein ursprünglicher Mach-Code mehr übrig; das war bereits Ende der 1990er abgeschlossen. FreeBSD lässt sich daher höchstens mit den sehr frühen Fork-/Grundlagenphasen von Mach in Verbindung bringen
      NetBSD und OpenBSD führten das zwar eine Zeit lang fort, stießen aber bei Performance, SMP/Skalierbarkeit und Networking an die Grenzen des Mach-Designs und schrieben es mit UVM (Unified Virtual Memory), entworfen und geleitet von Chuck Cranor, vollständig neu. OpenBSD übernahm diese Implementierung später und nutzt sie bis heute
      Unter den noch lebenden BSDs[1] nutzt nur XNU/Darwin weiterhin Mach, und selbst dort ist es nicht Mach 2.5, sondern Mach 3. Es gab Mach 2.5, 3 und 4 (GNU/Hurd nutzt Mach 4), aber die Kompatibilität ist gering; hauptsächlich teilen sie Einflüsse auf der Ebene der Gesamtarchitektur. Daher sollte man sie besser als separate Designs mit gemeinsamem Einfluss betrachten
      [0] Ursprünglich gab es ohnehin nicht viele dieser Spuren
      [1] Ich bin mir nicht einmal sicher, ob DragonBSD derzeit tot oder lebendig ist
  • Interessant an Darwin ist, wie radikal schnell sich zentrale Komponenten ändern. Das Aufgeben der Abwärtskompatibilität bei Systemaufrufen, verpflichtendes Code-Signing, bis hin zum dyld_shared_cache, der einzelne Systembibliotheksdateien abschaffte, um das Laden dynamischer Executables zu beschleunigen: Das ist ein ergebnisorientiertes Design ohne Nostalgie oder unantastbare Bereiche
    Das wirkt wie ein Ansatz, den nur ein großer Hardwarehersteller wie Apple durchziehen kann

  • Im Artikel heißt es, dass ein Pager-Daemon zur Verwaltung der Swap-Dateien im User Space läuft und dass auch Kernel-Speicher ausgelagert werden kann; erklärt wird aber nicht, wie ein User-Space-Daemon Kernel-Speicher auslagert
    Ich frage mich, ob es dafür eine hartcodierte Ausnahme für einen speziellen Daemon gibt oder ob spezielle Systemaufrufe verwendet werden. Wo kann man mehr über die konkreten Details der Speicherverwaltung im User Space lesen?

    • Diese Beschreibung ist ungenau und vermischt mehrere Dinge. Der Mach-Mikrokernel unterstützte ursprünglich echtes User-Space-Paging, ähnlich wie mmap, bei dem anstelle eines Dateisystems ein beliebiger Daemon eingesetzt werden konnte; die Schnittstelle ist hier zu sehen:
      https://web.mit.edu/darwin/src/modules/xnu/osfmk/man/memory_...
      Allerdings ist unklar, ob Darwin diese Funktion jemals tatsächlich genutzt hat; zumindest in den vergangenen rund 20 Jahren wurde sie nicht verwendet. dynamic_pager hat diese Schnittstelle nie genutzt. Stattdessen verwendete es eine deutlich eingeschränktere Mach-Schnittstelle: Wenn XNU Swap-Knappheit meldete, erstellte es Swap-Dateien und übergab sie mit den Systemaufrufen macx_swapon und macx_swapoff an den Kernel. Das eigentliche Swapping erledigte der Kernel; der alte dynamic_pager-Code ist hier:
      https://github.com/apple-oss-distributions/system_cmds/blob/...
      Auch diese Funktion ist inzwischen in den Kernel gewandert, sodass dynamic_pager heute im Grunde fast nichts mehr tut:
      https://github.com/apple-oss-distributions/system_cmds/blob/...
      Der Großteil des Kernel-Speichers ist wired und kann nicht ausgelagert werden, aber der Kernel kann etwa mit IOMallocPageable explizit auslagerbaren Speicher anfordern, der dann auf die Festplatte geswappt werden kann. Das wird allerdings kaum genutzt, und solcher Code muss sorgfältig sein, um Deadlocks zu vermeiden. Auch wenn der User Space nicht mehr direkt am „Paging“ selbst beteiligt ist, kommt es häufig vor, dass User Space ein oder zwei Schichten darunter beteiligt ist, etwa bei FSKit- oder FUSE-basierten User-Space-Dateisystemen, Dateisystemen auf Disk-Images oder NFS/SMB über User-Space-Networking-Extensions. Der letzte Teil könnte allerdings falsch sein: Dateisysteme, die im User Space blockieren, sind sicher möglich, aber Swap auf einem solchen Dateisystem zu platzieren, wird möglicherweise nicht unterstützt
    • https://github.com/apple-oss-distributions/xnu
  • Immer wenn ich etwas über den Darwin-Kernel lese, frage ich mich, wie anders es gewesen wäre, wenn Apple einfach Linux geforkt und darauf seine OS-Services aufgebaut hätte.
    Besonders wenn man sieht, wie sehr Apple an Darwin festhält, wirkt es auf mich ungünstig: Das, was Open Source verloren hat, und der Ertrag im Verhältnis zu Zeit und Kosten, die Apple investieren muss, scheinen in keinem Gleichgewicht zu stehen.

    • Für Apple gab es keinen passenden Zeitpunkt für so einen Wechsel. NeXTSTEP kam vor Linux heraus, und beim Umstieg auf Mac OS X hätte Apple neben allem anderen nicht auch noch ein Projekt stemmen können, bei dem der Kernel komplett ausgetauscht wird.
      Linux war Ende der 1990er auch nicht eindeutig die bessere Wahl. Und nachdem OS X nach einigen Versionen zum erfolgreichsten UNIX-artigen OS auf Consumer-PCs geworden war, hätte ein Wechsel auf eine Linux-Basis kurzfristig kaum Vorteile gebracht, aber hohe Kosten und Risiken.
      Hätte Apple das klassische MacOS noch fünf Jahre länger durchgeschleppt oder wäre Linux fünf Jahre früher ausgereift gewesen, hätte der OS-X-Umstieg ganz anders aussehen können. Aber XNU zugunsten eines Linux-Kernels vor 2.6 aufzugeben, ergab keinen Sinn.
    • Als Apple NeXT übernahm, war Linux mitten in der Entwicklung und noch nicht gut etabliert. Da Linux ein monolithischer Kernel war, bot es auch nicht das Maß an Abschottung, das Mach bereitstellte.
      Aus heutiger Sicht vereint FreeBSD viele der Vorteile von Darwin mit dem Linux-artigen Open-Source-Charakter. Wer eine sicherere Umgebung ohne Apples immer stärkere Abhängigkeiten möchte, sollte FreeBSD und andere BSDs ebenfalls als Zielplattformen in Betracht ziehen.
    • Interessanterweise trug Apple im 1996 gestarteten MkLinux-Projekt dazu bei, Linux auf PowerPC-Macs zu portieren. Das geschah noch vor der NeXT-Übernahme Ende desselben Jahres:
      https://en.m.wikipedia.org/wiki/MkLinux
      Es scheint keine Arbeit gegeben zu haben, die Macintosh-GUI und das Anwendungsökosystem auf Linux zu bringen. Allerdings ließ Apple schon vor der NeXT-Übernahme mit A/UX für 68k-Macs und später mit der Macintosh Application Environment für Solaris und HP-UX eine Macintosh-Umgebung auf Unix laufen; Letztere führte Mac OS als Unix-Prozess aus. Wenn ich mich richtig erinnere, wurde die Arbeit an der Macintosh Application Environment zur Grundlage der Blue Box von Rhapsody und später der Classic-Umgebung von Mac OS X. Theoretisch könnte man sich auch vorstellen, die Macintosh Application Environment auf MkLinux zu portieren. 1996 existierten nach der Einigung in den BSD-bezogenen Rechtsstreitigkeiten bereits moderne freie Open-Source-BSDs.
      Natürlich war es Mitte der 1990er als Consumer-Desktop-Strategie nicht realistisch, das klassische Mac OS als Prozess auf einem modernen OS wie Linux, FreeBSD, BeOS oder Windows NT laufen zu lassen. Dafür waren Workstation-Ressourcen nötig, während Apple weiterhin 68k-Macs unterstützte und Mac OS 8 auch noch auf einigen 68030/68040-Geräten lief. In der G3/G4-Ära wäre das realistischer gewesen, und in den 2000ern hätte man jedes klassische Macintosh-Programm auch als separaten Mac-OS-Prozess auf einem modernen OS ausführen können. Ohne Jobs’ Rückkehr hätte Apple aber wohl 1998 nicht überlebt. Außerdem brachte die NeXT-Übernahme Cocoa, IOKit, Quartz (den Nachfolger von Display PostScript) und weitere heute zentrale Technologien auf den Mac.
    • Ich weiß nicht, warum man sich eine noch stärkere Monokultur wünschen sollte. Es liegen schon jetzt zu viele Eier in einem Korb. Bei Kerneln hoffe ich eher auf mehr Vielfalt als auf weitere Vereinheitlichung.
      Anders betrachtet fühlt sich das ähnlich an wie der Vorschlag, Apple solle Safari auf Chromium umstellen.
    • XNU ist nur teilweise Open Source. Der Kern ist öffentlich, aber wichtige Teile wie das APFS-Dateisystem fehlen.
      Hätte Apple Linux geforkt, hätte das Unternehmen womöglich rechtlich alle Kernel-Module als Open Source veröffentlichen müssen. Für die Menschheit wäre das wahrscheinlich positiv gewesen, aber es wäre wohl nicht die Richtung gewesen, die Apple wollte.
  • In diesen Artikel sind viel Liebe und viel Arbeit geflossen. Aus meiner Sicht, nachdem ich den Großteil dieser Geschichte miterlebt, NeXTSTEP-Code nach Windows portiert, mich mit GNUSteps Rekonstruktionsversuchen beschäftigt, YellowBox und OpenStep noch in Erinnerung habe, Bücher über interne Strukturen gelesen und WWDC-Inhalte kontinuierlich verfolgt habe, passt das ziemlich genau zu meiner Erinnerung daran, wie sich die verschiedenen Systeme entwickelt haben.

  • Jobs versuchte, Torvalds für die Arbeit an Mac OS X anzuwerben, und Linus lehnte ab: https://www.macrumors.com/2012/03/22/steve-jobs-tried-to-hir...

    • Ausgerechnet Torvalds an einem Mikrokernel arbeiten zu sehen, ist schwer vorstellbar.
  • Ich bin mir nicht sicher, ob I/O Kit nur aus Geschwindigkeitsgründen in dieser C++-Teilmenge geschrieben wurde. Damals war das umstritten. Denn als Apple MacOS X ankündigte, hieß es, es sei nicht mit bestehender Software kompatibel und alle Partner müssten alles in Objective-C neu schreiben.
    Weil die Reaktion darauf schlecht war, ruderte Apple zurück und führte Carbon, eine API-Schicht für C++-Anwendungen, sowie Core Foundation ein, die Grundlage der Objective-C-basierten Foundation. Deshalb gibt es auch Obj-C++. Interessant ist, dass die Speicherverwaltung per Toll-free Bridging gestaltet wurde. Das heißt, in der C/C++-Welt allokierte Objekte können ohne zusätzlichen Overhead an Obj-C übergeben werden.

    • IOKit-C++ läuft im Kernel und hat daher wenig mit den genannten Technologien zu tun. Diese sind alle ausschließlich für den User Space gedacht.
  • Apple hätte rund um XNU eine bessere freie Open-Source-Community aufbauen sollen. Selbst jetzt, nach dem Wechsel zu ARM, hätte es eine auf x64 lauffähige Distribution geben sollen.

  • Ich wollte Darwin schon immer in dieser Tiefe verstehen; das war ein guter Artikel.

    • Singhs Mac OS X Internals ist eines meiner Lieblingsbücher. Es ist eine hervorragende Analyse, die die Zeit von Mac OS X 10.4 sehr tiefgehend behandelt, und ich wünschte wirklich, es gäbe eine aktualisierte Version.
      Am Ende dieses Artikels wird es ebenfalls zitiert. Es ist ein Material, das in der Geschichte von macOS lange Bestand haben wird.
    • Auch Windows NT hätte ich gern in dieser Tiefe verstanden. Also Win32 überspringen und das behandeln, was darunter liegt. Soweit ich es verstehe, ist Win32 nur eine Personality; außerdem gab es zu Zeiten von Windows XP die Windows Services for UNIX und in Windows Vista das Subsystem for UNIX-based Applications.
      Der zugrunde liegende NT-Kernel ist flexibel genug, um POSIX-Konformität zu ermöglichen; ein Artikel darüber wäre interessant.
  • Eine gute historische Zusammenfassung, aber sie überspringt viele der hervorragenden Sicherheitsarbeiten, die Apples Betriebssysteme von Linux oder Windows unterscheiden. Mir scheint, dass nicht richtig gewürdigt wird, wie weit Apple in Sachen Sicherheit heute voraus ist. Vielleicht wächst diese Wahrnehmung eines Tages so weit, dass CISOs für Menschen, die in sensiblen Umgebungen arbeiten, die Nutzung von Macs verlangen werden.
    Der Kern ist das Code-Signing-System. Es kann Apps Berechtigungen geben oder sie in eine Sandbox sperren und sorgt dafür, dass diese Durchsetzung tatsächlich Bestand hat. Apple verwendet nicht wie die meisten UNIX-Systeme ELF, sondern ein Format namens Mach-O. Die Unterschiede zwischen ELF und Mach-O sind größtenteils unwichtig, entscheidend ist aber, dass Mach-O zusätzliche Sections unterstützt, die ein signiertes Code Directory enthalten. Das Code Directory enthält Hashes der Code-Pages; der Kernel versteht diese Datenstruktur bis zu einem gewissen Grad, und dyld kann sie beim Laden eines Binaries oder einer Library verknüpfen. XNU prüft die Signatur des Code Directory, und das VMM-Subsystem berechnet beim Laden von Code-Pages bei Bedarf deren Hash und verifiziert, dass er mit dem signierten Hash im Directory übereinstimmt. Dadurch kann der Code-Directory-Hash als eindeutiger Identifikator für praktisch jedes Programm im Apple-Ökosystem dienen. Es gibt dabei einen Bug: Diese Verknüpfung hängt an der Mach-vnode-Struktur. Wenn man ein signiertes Binary überschreibt und danach ausführt, wird der Kernel ärgerlich und beendet den Prozess, selbst wenn die Signatur der neuen Datei gültig ist. Damit er die neue Situation erkennt, muss man die Datei tatsächlich vollständig ersetzen.
    Auf diese Grundlage setzt Apple Code Requirements. Das sind Programme in einer kleinen Ausdruckssprache, mit der Einschränkungen für verschiedene Eigenschaften einer Codesignatur formuliert werden. Man kann Anforderungen ausdrücken wie „dieses Binary muss von Apple signiert sein“, „jede Version dieses Binaries ist erlaubt, sofern sie von einem Subjekt mit Identität X nach Zertifizierungsstelle Y signiert wurde“, oder „dieses Binary muss cdhash Z haben“, also genau dieses Binary sein. Ein Binary kann außerdem ein Designated Requirement offenlegen, das anderen Parteien mitteilt, über welche Anforderung es identifiziert werden möchte. Das wirkt zunächst übertrieben, ermöglicht aber, dass ein Programm auch während seiner Weiterentwicklung eine stabile und nicht fälschbare Identität behält.
    Der Kernel stellt die Signaturidentität eines Tasks anderen Tasks über Ports zur Verfügung. Eine Userspace-Library kann die Constraint-Sprache interpretieren und Anforderungen an diesen Port stellen. Wenn ein Programm zum Beispiel einen Schlüssel im System-Keychain speichert, prüft der im Userspace implementierte keychain-Daemon das Designated Requirement des Programms, das den RPC gesendet hat, und vergleicht es später mit Anfragen zur Nutzung des Schlüssels.
    Dieses System wird über Entitlements abstrahiert. Entitlements sind key=value-Paare, die Berechtigungen ausdrücken. Da es ein offenes System ist, können Apps auch eigene Entitlements definieren, die meisten werden jedoch von Apple festgelegt. Einige sind rein opt-in: Wenn man sie anfordert, erlaubt das OS sie automatisch und stillschweigend. Das wirkt anfangs nutzlos, ermöglicht dem App Store aber, vorab zu beschreiben, was eine App tun wird, und ganz allgemein eine Least-Privilege-Haltung, bei der Apps nicht auf Dinge zugreifen können, die sie nicht brauchen. Einige Entitlements benötigen zusätzliche Nachweise wie Provisioning Profiles. Das sind von Apple bereitgestellte signierte CMS-Datenstrukturen, die grob bedeuten: „Eine App mit Designated Requirement X darf das eingeschränkte Entitlement Y verwenden“; dafür braucht man also Apples Erlaubnis. Wieder andere werden praktisch als allgemeines System für Signatur-Flags missbraucht und haben nichts mit Sicherheit zu tun.
    Das System wird durch die Zusammenarbeit von Userspace und XNU weiter ausgebaut. Ein Binary signieren zu können, ist nur der Anfang; viele Programme haben auch Datendateien. An dieser Stelle wirkt Apples Sicherheitssystem etwas geflickt. Der Kernel ist nicht an Integritätsprüfungen von Datendateien beteiligt. Stattdessen liegt an einer speziellen Stelle in der etwas willkürlichen Bundle-Verzeichnisstruktur eine plist; diese plist enthält pro Datei die Hashes aller Datendateien im Bundle, der Hash der plist geht in die Codesignatur ein, und schließlich prüft Gatekeeper beim ersten Start das Ganze. Der Kernel fragt Gatekeeper, ob die Ausführung des Programms erlaubt werden soll, und Gatekeeper entscheidet anhand des Vorhandenseins eines Extended Attributes, das an der Datei hängt und von GUI-Tools wie Webbrowsern oder Entpackprogrammen weitergegeben wird. Userspace-OS-Code wie Finder sorgt dafür, dass Gatekeeper aufgerufen wird, wenn ein Programm erstmals heruntergeladen wurde; Gatekeeper hasht dann alle Dateien im Bundle und prüft, ob sie mit den im Binary signierten Inhalten übereinstimmen. Deshalb erscheint unter macOS beim ersten Start der langsame Dialog „Verifying app“. Das scheint ein Weg zu sein, Apps, die große Datendateien ohne mmap öffnen, nicht zum Hängen zu bringen; schade ist aber, dass auf schnellen Netzwerken die nicht optimierte Gatekeeper-Prüfung langsamer sein kann als der Download selbst. Apple scheint sich nicht besonders darum zu kümmern, weil es Distribution außerhalb des Stores als Legacy-Technik betrachtet.
    Schließlich gibt es Seatbelt. Das ist eine Lisp-basierte Programmiersprache zum Ausdrücken von Sandbox-Regeln. Diese Dateien werden im Userspace zu einer Art Bytecode kompiliert, den der Kernel auswertet. Die Sprache ist recht ausgefeilt und kann, vollständig auf Basis von Code-Signing-Identitäten, beliebige Regeln dafür ausdrücken, wie verschiedene Systemkomponenten miteinander interagieren und was sie tun dürfen.
    In diesem System gab es eine offensichtliche Lücke, die erst in jüngeren Releases geschlossen wurde: Datendateien können Code enthalten, werden aber nur einmal geprüft. Bei Electron- oder JVM-Apps ist das tatsächlich der Fall, weil sie Code in portablen Formaten enthalten. Eine App konnte daher Datendateien verändern, um Code in eine andere App einzuschleusen und Code Signing zu umgehen. In aktuellen macOS-Versionen sandboxed Seatbelt alle laufenden Apps, um das zu verhindern. Soweit ich weiß, gibt es in modernem macOS keinen Code außerhalb der Sandbox. Eine der Sandbox-Policies verhindert, dass Apps ohne Erlaubnis Datendateien anderer Apps verändern. Die Policies sind ziemlich ausgefeilt: Apps, die von derselben von Apple verifizierten juristischen Person signiert wurden, können einander verändern; eine App kann Änderungen durch andere Apps erlauben, die zu einem Code Requirement passen; und Nutzer können bei Bedarf Berechtigungen erteilen. Das lässt sich prüfen, indem man unter Settings -> Privacy & Security -> App Management die Berechtigung für Terminal.app deaktiviert, neu startet und dann einen Befehl wie vim /Applications/Google Chrome.app/Contents/Info.plist ausführt. Obwohl die Dateirechte rw sind, sieht vim die Datei als schreibgeschützt.
    Ab hier endet mein Verständnis, da ich nicht bei Apple arbeite. Soweit ich weiß, versteht der Kernel App-Bundles nicht, und ich bin mir nicht sicher, wie er entscheidet, ob ein open()-Systemaufruf auf read-only umgestellt wird. Meine Vermutung ist, dass die Standard-Seatbelt-Policy den Kernel veranlasst, einen Upcall an einen Security-Daemon zu machen, der das Bundle-Format und die SQLite-Berechtigungsdatenbank lesen kann, und dass dieser Daemon das Designated Requirement des öffnenden Prozesses mit dem Bundle und der durch die Sandbox ausgedrückten Policy vergleicht und danach entscheidet.

  • Ich halte es nicht für angemessen, eine solche Funktion Sicherheit zu nennen
    Meiner Meinung nach sollte sich Sicherheit immer auf den Schutz des Computerbesitzers oder -nutzers beziehen
    Solche Funktionen von Apple können zwar zur Verbesserung der Sicherheit eingesetzt werden, ihr primärer Designzweck besteht jedoch darin, dem Anbieter, der den Computer verkauft hat, stärkere Kontrolle darüber zu geben, wie der theoretische Besitzer ein Gerät nutzt, das eigentlich nicht mehr ihm gehören sollte. Anders gesagt: Es geht in die Richtung, Apple entscheiden zu lassen, welche Programme Endnutzer ausführen dürfen