2 puntos por GN⁺ 2025-04-06 | 1 comentarios | Compartir por WhatsApp
  • Darwin de Apple es la base tipo Unix de macOS, iOS y los sistemas operativos modernos de Apple, y XNU es un kernel híbrido que combina Mach y BSD dentro de un solo kernel
  • XNU mantiene las tareas, hilos, memoria virtual e IPC basado en puertos de Mach, mientras ubica los servicios BSD en el mismo espacio de direcciones del kernel para reducir el costo del paso de mensajes de un microkernel puro
  • El linaje de Mach 2.5+4.3BSD de NeXTSTEP continuó hacia Mac OS X y Darwin después de la adquisición de NeXT por Apple en 1996, y luego fue incorporando gradualmente código de FreeBSD, I/O Kit, 64 bits, ARM y soporte para Apple Silicon
  • macOS e iOS han ampliado funciones como sandboxing, firma de código, SIP, APFS, DriverKit, planificación con QoS, Jetsam y memoria comprimida mediante la cooperación entre el kernel y el espacio de usuario
  • La evolución de XNU se parece más a mantener la base Mach/BSD que a reescribir el kernel: integra dentro del kernel las partes donde el rendimiento es crítico y separa con IPC y espacio de usuario las partes que requieren aislamiento

El punto de partida de Darwin y XNU

  • Darwin es el sistema operativo central de tipo Unix que sostiene macOS, iOS y las plataformas modernas de Apple
  • En su núcleo está el kernel XNU, sigla de “X is Not Unix”, que combina un núcleo de microkernel Mach con componentes Unix BSD
  • Esta estructura busca equilibrar modularidad y rendimiento aprovechando a la vez el diseño basado en paso de mensajes de Mach y la estabilidad y compatibilidad POSIX de BSD

La historia: de Mach a NeXTSTEP y Mac OS X

  • Mach comenzó en 1985 como un proyecto liderado por Richard Rashid y Avie Tevanian en Carnegie Mellon University
    • Era un diseño de microkernel que buscaba dejar en el kernel solo funciones de bajo nivel como gestión de memoria, planificación de CPU e IPC, y mover el sistema de archivos, la red y los controladores a servidores en espacio de usuario
    • Conceptos como tareas, hilos, puertos Mach, copy-on-write y objetos de memoria se establecieron como objetos centrales del kernel
  • NeXTSTEP se lanzó en 1989 sobre el kernel Mach 2.5 con un subsistema Unix 4.3BSD por encima
    • NeXT priorizó el rendimiento y eligió integrar el código BSD dentro del espacio de direcciones del kernel en lugar de seguir un enfoque de microkernel puro
    • También incluía DriverKit basado en Objective-C, que luego dio continuidad al linaje XNU de Apple
  • Apple adquirió NeXT en 1996 y eligió NeXTSTEP como base del nuevo Mac OS X
    • Con el inicio del proyecto Rhapsody, el kernel híbrido Mach/BSD de NeXT pasó a Apple
    • Después, XNU incorporó código de la línea Mach 3.0 basado en OSFMK 7.3 y código de 4.4BSD y FreeBSD

Desarrollo inicial de Darwin y Mac OS X

  • Apple publicó una Developer Preview de Mac OS X en 1999 y en 2000 abrió Darwin 1.0, exponiendo a desarrolladores el kernel XNU y el espacio de usuario Unix básico
  • Mac OS X 10.0 Cheetah se lanzó comercialmente en 2001 basado en Darwin 1.3.1
  • Los primeros cambios se enfocaron en reforzar la capa BSD, la red, el sistema de archivos y el rendimiento de hilos
    • Mac OS X 10.1 Puma mejoró el rendimiento de la gestión de hilos y el soporte para hilos en tiempo real
    • Mac OS X 10.2 Jaguar incluyó IPv6, IPSec, mDNSResponder y journaling en HFS+
    • Mac OS X 10.3 Panther integró mejoras del kernel de FreeBSD 5 y bloqueos de kernel más granulares para fortalecer el uso multiprocesador
  • Mac OS X 10.4 Tiger obtuvo certificación UNIX 03, adoptó kqueue/kevent de FreeBSD y mantuvo una base multiplataforma para la transición a Intel Mac

Las exigencias móviles traídas por 64 bits y iPhone OS

  • Mac OS X 10.5 Leopard basado en Darwin 9 introdujo ejecución de kernel de 64 bits, controladores de 64 bits, ASLR, sandboxing y DTrace
  • El primer iPhone OS de 2007 también se lanzó sobre Darwin 9, extendiendo XNU a dispositivos móviles ARM
    • Como el iPhone original tenía RAM limitada y no podía usar swap, usó el mecanismo Jetsam para cerrar apps en segundo plano cuando la memoria escaseaba
    • iPhone OS ejecutaba apps de terceros dentro de un sandbox y exigía firma de código estricta para los binarios
  • Mac OS X 10.6 Snow Leopard eliminó el soporte para PowerPC y reforzó las optimizaciones de 64 bits y multinúcleo centradas en Intel
    • Grand Central Dispatch y libdispatch son bibliotecas de espacio de usuario, pero aprovechan el pool de hilos del kernel y su soporte de planificación
    • OpenCL también requería una integración estrecha entre frameworks de usuario y controladores del kernel para cómputo en GPU
  • iOS 4 ajustó el planificador para diferenciar prioridades de apps en segundo plano y adaptarse al soporte de SoC ARM multinúcleo

Ampliación de funciones del kernel en macOS e iOS modernos

  • OS X 10.9 Mavericks añadió memoria comprimida y timer coalescing
    • La memoria comprimida reduce el swap a disco comprimiendo páginas inactivas dentro de la RAM
    • Timer coalescing baja el consumo energético alineando los momentos en que se despierta la CPU
  • OS X 10.11 El Capitan introdujo System Integrity Protection, es decir, SIP
    • SIP es aplicado por el kernel a través del framework de Mandatory Access Control de la capa BSD, e impide que incluso procesos root modifiquen archivos y procesos críticos del sistema
  • macOS 10.13 High Sierra adoptó APFS como sistema de archivos predeterminado
    • La capa VFS de XNU se amplió para soportar snapshots, clonación y cifrado a nivel de contenedor de APFS
    • En la misma época, la carga de kexts de terceros empezó a requerir aprobación del usuario
  • macOS 10.15 Catalina introdujo el moderno DriverKit
    • DriverKit mueve muchos controladores fuera del kernel hacia Driver Extensions en espacio de usuario
    • El kernel ofrece acceso limitado al hardware a los controladores en espacio de usuario mediante IPC y memoria compartida
    • Catalina también introdujo un volumen del sistema de solo lectura para reforzar la protección de SIP

XNU en la era de Apple Silicon

  • macOS 11 Big Sur y Darwin 20, en 2020, fueron la primera versión en soportar Macs con Apple Silicon
  • XNU ya soportaba ARM por iOS, pero en Apple Silicon para Mac también tuvo que considerar la arquitectura heterogénea de CPU big.LITTLE
    • El planificador reconoce núcleos heterogéneos para poder ubicar hilos pesados y de alta prioridad en núcleos de rendimiento, y hilos de bajo QoS o en segundo plano en núcleos de eficiencia
    • Las clases de QoS también pueden servir como pista de planificación que influye en la selección del tipo de núcleo en Apple Silicon
  • En la arquitectura de memoria unificada de Apple Silicon, el administrador de memoria del kernel y los controladores de GPU gestionan el uso compartido de buffers
    • La abstracción Mach VM encaja con compartir objetos de memoria entre espacio de usuario y GPU mediante remapeo de VM en lugar de copias
  • El backend ARM64 soporta Pointer Authentication, usando claves PAC para marcos de excepción y punteros del sistema, ayudando a mitigar ataques ROP
  • XNU sigue siendo la base común de múltiples plataformas de Apple como macOS, iOS, watchOS, tvOS, bridgeOS y visionOS

La arquitectura de kernel híbrido de XNU

  • Los componentes Mach y BSD de XNU están enlazados en un solo binario de kernel y comparten el mismo espacio de direcciones
    • No existe una frontera de protección entre Mach y BSD, y dentro del kernel interactúan mediante llamadas de función normales, no con mensajes IPC
    • Una llamada al sistema Unix como read() no envía un mensaje a un servidor BSD aparte, sino que entra directamente al código BSD del sistema de archivos dentro del kernel
  • Mach se encarga de la infraestructura central del kernel
    • Gestiona creación y terminación de tareas e hilos, cambios de contexto, planificación de bajo nivel, locks, timers y colas de planificación
    • Cada proceso BSD corresponde a una tarea Mach y cada hilo corresponde a un hilo Mach
    • Mach VM ofrece mapas de direcciones virtuales, objetos de memoria, copy-on-write y memoria compartida basada en IPC
  • BSD aporta el carácter Unix y los servicios
    • Gestiona PID, ID de usuario, señales, hilos POSIX, sistemas de archivos, red, IPC Unix, I/O de dispositivos, permisos y frameworks de seguridad
    • VFS maneja sistemas de archivos como HFS+, APFS y NFS, y en archivos mapeados en memoria se conecta con Mach VM mediante el vnode pager
    • El sandboxing y SIP funcionan gracias a la cooperación entre módulos de seguridad BSD y restricciones sobre puertos de tarea de Mach
  • I/O Kit es el tercer eje de XNU: un framework orientado a objetos para controladores escrito en una forma restringida de C++
    • Representa dispositivos y controladores como jerarquías de clases, y los controladores se ejecutan dentro del kernel como objetos C++
    • En espacio de usuario ofrece acceso limitado mediante propiedades del I/O Registry y la interfaz user client
    • Hasta la llegada de DriverKit en macOS moderno, la mayoría de los controladores funcionaban dentro del kernel en forma de kext

Mach IPC y servicios del sistema

  • Aunque XNU no usa mensajes Mach en la ruta de llamadas al sistema Unix, sí utiliza ampliamente Mach IPC para servicios en espacio de usuario y para la comunicación entre kernel y procesos
  • Los puertos Mach se usan como handles en espacio de usuario para varios objetos del kernel
    • Cada tarea tiene un task port, y un proceso con privilegios puede usarlo para inspeccionar o controlar otras tareas
  • Los eventos y notificaciones también se entregan mediante mensajes Mach
    • WindowServer recibe eventos de entrada del usuario desde el kernel como mensajes Mach
    • Grand Central Dispatch usa internamente puertos Mach para dormir hilos que esperan eventos
    • kqueue/kevent puede esperar al mismo tiempo mensajes de puertos Mach y file descriptors
  • El framework XPC de Apple está construido sobre mensajes Mach
    • Las conexiones XPC se basan internamente en puertos Mach
    • El modelo de permisos de los puertos Mach se usa para verificar privilegios del llamante en servicios como securityd del Keychain
    • Los mensajes Mach pueden transferir memoria out-of-line y derechos sobre puertos, por lo que sirven para construir RPC de alto nivel
  • MIG, es decir, Mach Interface Generator, se usa para generar definiciones de interfaz y código de envío y recepción de mensajes entre kernel y espacio de usuario

El planificador y la gestión de hilos

  • El planificador de XNU partió del planificador round-robin basado en prioridades de Mach, pero fue muy modificado para adaptarse a necesidades de escritorio y móviles
  • Históricamente, Mach definía prioridades de hilo en el rango 0~127, y XNU usa valores como sched_pri y base_pri
    • Los hilos de time-sharing pueden cambiar de prioridad según su uso
    • Los hilos en tiempo real usan prioridad fija
  • XNU maneja eficiencia y balanceo de carga mediante colas de ejecución por CPU e interrupciones del planificador
  • El sandbox de apps y la ejecución en segundo plano en iOS se reflejan en el planificador mediante conceptos como roles de trabajo o grupos de prioridad
  • Las clases de QoS se integraron en la planificación a partir de iOS 8 y OS X 10.10
    • Clases como user-interactive, user-initiated, default, utility y background influyen en bandas de prioridad y en la planificación
    • Los hilos creados con Grand Central Dispatch o NSThread heredan QoS
    • En Apple Silicon, hilos con QoS background pueden colocarse en núcleos de eficiencia
  • También soporta colas en tiempo real y planificación basada en deadlines para audio en tiempo real y trabajos críticos

Gestión de memoria y Mach VM

  • La gestión de memoria de XNU gira en torno al subsistema Mach VM
  • Cada tarea Mach tiene un espacio de direcciones virtuales representado por un VM map y regiones VM
    • fork() usa copy-on-write en lugar de copiar de inmediato toda la memoria
    • Padre e hijo comparten las mismas páginas hasta que alguno escriba en ellas
  • Mach usa los conceptos de objetos de memoria y pager
    • El pager predeterminado para memoria anónima lo maneja el demonio de espacio de usuario dynamic_pager, que administra archivos de swap cuando hace falta
    • Para memoria de archivos, el vnode pager en la capa BSD dentro del kernel interactúa con el código del sistema de archivos
  • La memoria comprimida de Mavericks se implementó agregando un compression pager dentro del kernel
    • Cuando aumenta la presión de memoria, las páginas inactivas no se envían de inmediato al disco, sino que se almacenan comprimidas en un compressor pool dentro de la RAM
    • Si la compresión no alcanza, entonces se usa swap en disco
  • La gestión de memoria física está a cargo de pmap, la capa dependiente de la arquitectura
    • pmap administra las tablas de páginas o las estructuras equivalentes de la arquitectura correspondiente
    • En ARM64, funciones de seguridad y cuestiones relacionadas con caché también están vinculadas con pmap
  • La shared cache de dyld se aprovecha eficientemente al mapear las mismas páginas físicas como solo lectura en múltiples procesos

Soporte de virtualización

  • En Intel Mac, desde OS X 10.10 se ofrece Hypervisor.framework para virtualización en espacio de usuario
    • Usa Intel VT-x para permitir que un proceso en espacio de usuario funcione como monitor de máquina virtual
    • Herramientas como xhyve y algunas apps de virtualización aprovechan esta función
  • En Apple Silicon, Virtualization.framework de macOS 11 funciona sobre un hipervisor dentro del kernel para ARM64
    • Los desarrolladores pueden ejecutar VMs de Linux o macOS desde espacio de usuario
    • En lugar de permitir hipervisores arbitrarios de terceros dentro del kernel, Apple usa un modelo de acceso mediante frameworks y permisos propios
  • Desde la perspectiva del kernel, las funciones del hipervisor incluyen gestión de memoria física del huésped, trap-and-emulate de instrucciones sensibles y exposición de interfaces vCPU
  • El planificador Mach programa las vCPU, que desde el host son hilos, y el subsistema de memoria se usa para mapear la memoria del huésped
  • En iOS también es posible la virtualización bajo ciertas condiciones y permisos, y hubo casos de dispositivos A14 con jailbreak donde se habilitó el hipervisor para ejecutar VMs de Linux

Secure Enclave y Exclaves

  • macOS usa dos mecanismos de aislamiento, Secure Enclave y exclaves, para proteger tareas y datos sensibles
  • Secure Enclave es un subsistema dedicado y endurecido integrado en los SoC de Apple
    • Está presente en iPhone, iPad, T2 y Macs con Apple Silicon, entre otros
    • Ejecuta su propio sistema operativo basado en microkernel y gestiona información sensible como claves criptográficas y datos biométricos
    • Su objetivo es aislar datos importantes incluso si el procesador principal de aplicaciones o el kernel son comprometidos
  • Exclaves es una arquitectura de seguridad más reciente aparecida en macOS 14.4 e iOS 17
    • En vez de dejar tareas sensibles dentro del mismo dominio de privilegios que el kernel principal XNU, separa algunos recursos clave en un dominio “externally located” independiente
    • Entre los recursos objetivo están servicios de Apple ID, buffers de audio, datos de sensores y componentes de control de indicadores
    • Su gestión involucra kexts especiales y frameworks privados como ExclaveKextClient.kext, ExclaveSEPManagerProxy.kext y ExclavesAudioKext.kext
  • Esta separación agrega una capa extra de defensa al aislar el trabajo dentro del exclave incluso si el kernel principal es comprometido

Dirección de diseño a largo plazo

  • Darwin y XNU son un diseño mixto: no un microkernel completo ni un kernel monolítico completo
  • El núcleo basado en Mach ayudó a adaptarse a nuevas arquitecturas y funciones del sistema, mientras que la capa BSD aporta un entorno compatible con POSIX y herramientas y APIs Unix
  • Apple absorbió transiciones de CPU desde PowerPC a Intel y ARM, así como nuevas categorías de dispositivos como iPhone, Apple Watch y Apple Vision Pro, sobre la base de XNU
  • Los cambios en el kernel han avanzado principalmente de tres maneras
    • Las nuevas funciones se extienden sobre el kernel existente
    • Los componentes donde el rendimiento es crucial se integran dentro del kernel
    • Los componentes que requieren aislamiento se separan mediante Mach IPC y espacio de usuario
  • Las publicaciones de código abierto de Darwin ofrecen a investigadores una ventana para estudiar un kernel híbrido comercial, aunque con límites en lo que se hace público

1 comentarios

 
GN⁺ 2025-04-06
Opiniones en Hacker News
  • El sistema de memoria virtual de Mach llegó no solo a 4.4BSD y FreeBSD, sino también a NetBSD[0] y OpenBSD[1], aunque al parecer no a 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...

    • Para ser precisos, no es así. 386BSD, FreeBSD y NetBSD heredaron al principio un diseño al estilo de Mach 2.5, pero FreeBSD reemplazó bastante rápido todos los restos de Mach VM[0] por una reescritura moderna y de buen rendimiento de la VM
      Para la época de FreeBSD 4 ya no quedaba código original de Mach en la base de código del kernel; eso ya se había terminado a fines de los años 90, así que lo único que vincula a FreeBSD con Mach es una etapa muy temprana de bifurcación/base
      NetBSD y OpenBSD también siguieron un tiempo, pero chocaron con las limitaciones de rendimiento, SMP/escalabilidad y redes del diseño de Mach, y lo reescribieron por completo como UVM (memoria virtual unificada), diseñada y liderada por Chuck Cranor. OpenBSD tomó después esa implementación y todavía la usa
      Entre los BSD vivos[1], el único que sigue usando Mach es XNU/Darwin, y aun así no es Mach 2.5, sino Mach 3. Hubo Mach 2.5, 3 y 4 (GNU/Hurd usa Mach 4), pero la compatibilidad entre ellos es baja y comparten sobre todo una influencia a nivel de arquitectura general, así que conviene verlos como diseños separados con una influencia común
      [0] Para empezar, tampoco había tantos restos
      [1] No estoy muy seguro de si DragonBSD está muerto o sigue vivo
  • Lo interesante de Darwin es la velocidad con la que cambian radicalmente sus componentes centrales. Desde abandonar la compatibilidad hacia atrás de las llamadas al sistema, pasando por la firma de código obligatoria, hasta dyld_shared_cache, que eliminó los archivos individuales de bibliotecas del sistema para acelerar la carga de ejecutables dinámicos: es una forma de diseñar orientada a resultados, sin nostalgia ni vacas sagradas
    Parece un enfoque que solo una gran empresa de hardware como Apple puede llevar a cabo

  • El artículo dice que el demonio pager que administra los archivos de swap corre en espacio de usuario y que también se puede hacer swap de la memoria del kernel, pero no explica cómo un demonio en espacio de usuario hace swap de memoria del kernel
    Me pregunto si hay una excepción hardcodeada para un demonio especial o si usa llamadas al sistema especiales. ¿Dónde se puede ver más sobre los detalles de la administración de memoria en espacio de usuario?

    • Esa explicación es imprecisa y mezcla varias cosas. El microkernel Mach originalmente admitía paginación real en espacio de usuario, parecida a mmap, en la que se ponía un demonio arbitrario en lugar de un sistema de archivos; la interfaz se puede ver aquí:
      https://web.mit.edu/darwin/src/modules/xnu/osfmk/man/memory_...
      Sin embargo, no está claro si Darwin llegó a usar esta función en la práctica y, al menos durante los últimos ~20 años, no la ha usado. dynamic_pager nunca usó esta interfaz; cuando XNU avisaba que faltaba swap, creaba archivos de swap y los pasaba al kernel con las llamadas al sistema macx_swapon y macx_swapoff, usando una interfaz de Mach mucho más limitada. El swap real lo hacía el kernel, y el código antiguo de dynamic_pager está aquí:
      https://github.com/apple-oss-distributions/system_cmds/blob/...
      Esa función ahora también pasó al kernel, así que el dynamic_pager actual prácticamente no hace casi nada:
      https://github.com/apple-oss-distributions/system_cmds/blob/...
      La mayor parte de la memoria del kernel está fijada (wired) y no se puede paginar, pero el kernel sí puede pedir explícitamente memoria paginable con mecanismos como IOMallocPageable, y esa memoria puede enviarse a disco como swap. Aun así, se usa muy poco, y ese tipo de código debe tener cuidado para evitar interbloqueos. Aunque el espacio de usuario ya no participe en la “paginación” en sí, es común que intervenga una o dos capas más abajo, como en sistemas de archivos en espacio de usuario basados en FSKit o FUSE, sistemas de archivos sobre imágenes de disco, o NFS/SMB que pasan por extensiones de red en espacio de usuario. Aunque puede que me equivoque en esta última parte. Sin duda son posibles los sistemas de archivos que quedan bloqueados por el espacio de usuario, pero quizá no se admita poner swap sobre un sistema de archivos así
    • https://github.com/apple-oss-distributions/xnu
  • Cada vez que veo la historia del kernel Darwin, me pregunto qué tan distinto habría sido todo si Apple simplemente hubiera hecho un fork de Linux y montado sus servicios de OS encima.
    En especial, al ver cuánto se aferra Apple a Darwin, me queda una mala impresión: lo que perdió el open source y el retorno frente al tiempo y costo que Apple tiene que invertir parecen estar desequilibrados.

    • Apple nunca tuvo un momento adecuado para hacer una transición así. NeXTSTEP salió antes que Linux, y cuando se mudó a Mac OS X, Apple no podía asumir, además de todo lo demás, un proyecto para reemplazar por completo el kernel.
      El Linux de fines de los 90 tampoco era claramente una mejor opción, y después de que OS X pasara por varias versiones y se consolidara como el sistema tipo UNIX más exitoso en PCs de consumo, cambiarlo a una base Linux habría tenido casi ningún beneficio de corto plazo y sí muchos costos y riesgos.
      Si Apple hubiera estirado el MacOS clásico cinco años más, o si Linux hubiera madurado cinco años antes, la transición a OS X podría haber sido muy distinta. Pero abandonar XNU por un kernel Linux previo a 2.6 no tenía sentido.
    • En la época en que Apple compró NeXT, Linux estaba en pleno desarrollo y todavía no estaba bien establecido. Al ser un kernel monolítico, Linux tampoco ofrecía el nivel de compartimentación que ofrecía Mach.
      Con los criterios actuales, FreeBSD combina muchas de las ventajas de Darwin con el carácter open source al estilo Linux. Si se quiere un entorno más seguro sin la dependencia cada vez mayor de Apple, FreeBSD y otros BSD también valen la pena como objetivos de distribución.
    • Curiosamente, Apple contribuyó al port de Linux a las Mac PowerPC en el proyecto MkLinux, iniciado en 1996. Eso fue antes de la compra de NeXT a fines de ese mismo año:
      https://en.m.wikipedia.org/wiki/MkLinux
      No parece que haya habido trabajo para llevar la GUI de Macintosh y el ecosistema de aplicaciones a Linux. Sin embargo, incluso antes de comprar NeXT, Apple ya ejecutaba el entorno Macintosh sobre Unix con A/UX para Mac 68k y, más tarde, con Macintosh Application Environment para Solaris y HP-UX; este último ejecutaba Mac OS como un proceso Unix. Si no recuerdo mal, el trabajo de Macintosh Application Environment sirvió de base para Blue Box en Rhapsody y, después, para el entorno Classic de Mac OS X. En teoría, también se podría imaginar un port de Macintosh Application Environment a MkLinux. En 1996, tras el acuerdo de las demandas relacionadas con BSD, los BSD modernos libres y open source ya existían.
      Por supuesto, a mediados de los 90, ejecutar el Mac OS clásico como un proceso sobre un OS moderno como Linux, FreeBSD, BeOS o Windows NT no era realista como estrategia de escritorio para consumidores. Hacían falta recursos de estación de trabajo, mientras Apple seguía dando soporte a Macs 68k, y Mac OS 8 todavía corría en algunos equipos 68030/68040. En la era G3/G4 habría sido más realista, y en los 2000 habría sido posible ejecutar cada programa clásico de Macintosh como un proceso separado de Mac OS sobre un OS moderno, pero sin el regreso de Jobs Apple no habría sobrevivido más allá de 1998. Además, la compra de NeXT también llevó a la Mac Cocoa, IOKit, Quartz (sucesor de Display PostScript) y otras tecnologías que hoy son centrales.
    • No entiendo por qué deberíamos querer una monocultura todavía peor. Ya pusimos demasiados huevos en una sola canasta. Ojalá los kernels se diversifiquen más, no que se integren todavía más.
      Visto de otra manera, me suena parecido a proponer que Apple mueva Safari sobre Chromium.
    • XNU solo es open source en parte. Lo central está publicado, pero faltan piezas importantes como el sistema de archivos APFS.
      Si hubieran hecho un fork de Linux, legalmente quizás habrían tenido que publicar como open source todos los módulos del kernel. Probablemente habría sido positivo para la humanidad, pero no habría sido la dirección que Apple quería.
  • Este texto tiene mucho cariño y mucho trabajo detrás. Como alguien que vivió gran parte de esa historia, porteó código de NeXTSTEP a Windows, investigó a fondo el intento de recreación de GNUStep, recuerda YellowBox y OpenStep, leyó libros sobre las estructuras internas y ha seguido con constancia el contenido de WWDC, encaja casi por completo con mi recuerdo de cómo evolucionaron los distintos sistemas.

  • Jobs intentó reclutar a Torvalds para trabajar en Mac OS X, y Linus lo rechazó: https://www.macrumors.com/2012/03/22/steve-jobs-tried-to-hir...

    • Justo imaginar a Torvalds trabajando en un microkernel cuesta bastante.
  • No estoy seguro de que I/O Kit esté escrito en ese subconjunto de C++ solo por velocidad. En esa época hubo polémica, porque cuando Apple presentó MacOS X dijo que no sería compatible con el software existente y que todos sus socios tendrían que reescribirlo en Objective-C.
    Cuando la reacción fue negativa, Apple dio marcha atrás e introdujo Carbon, una capa de API para aplicaciones C++, y Core Foundation, la base subyacente de Foundation basada en Objective-C. Esa también es la razón por la que existe Obj-C++. Lo interesante es que hicieron que la gestión de memoria fuera toll-free bridging. Es decir, los objetos asignados en el mundo C/C++ pueden pasarse a Obj-C sin overhead adicional.

    • IOKit C++ se ejecuta en el kernel, así que no tiene mucha relación con las tecnologías mencionadas. Todas ellas son tecnologías solo de espacio de usuario.
  • Apple debería haber cultivado una mejor comunidad libre y open source alrededor de XNU. Incluso ahora que se pasó a ARM, debería haber existido una distribución ejecutable en x64.

  • Quería entender Darwin con este nivel de profundidad, y fue un muy buen artículo

    • Mac OS X Internals de Singh es uno de mis libros favoritos. Es un excelente análisis que cubre con muchísima profundidad la época de Mac OS X 10.4, así que de verdad me gustaría que existiera una versión actualizada
      Veo que también lo citan al final de este artículo. Es un material que perdurará en la historia de macOS
    • También quería entender Windows NT con este nivel de profundidad. Algo que se saltara Win32 y tratara lo que hay debajo. Según entiendo, Win32 es solo una personalidad, y también existieron Windows Services for UNIX en la época de Windows XP y Subsystem for UNIX-based Applications en Windows Vista
      El kernel NT subyacente es lo bastante flexible como para permitir el cumplimiento de POSIX, así que tratar estos temas daría para un artículo interesante
  • Es un buen repaso histórico, pero se saltea mucho del excelente trabajo de seguridad que distingue a los sistemas operativos de Apple de Linux o Windows. Parece que no se valora bien qué tan adelantada está Apple hoy en materia de seguridad. Quizás algún día esa percepción crezca y quienes trabajan en entornos sensibles terminen con un CISO que les exija usar Mac
    La clave es el sistema de firma de código. Permite otorgar permisos a las apps o encerrarlas en un sandbox, y hace que esa imposición realmente se mantenga. Apple no usa ELF como la mayoría de los UNIX, sino un formato llamado Mach-O. La mayoría de las diferencias entre ELF y Mach-O no importan, pero sí es importante que Mach-O admite una sección adicional que contiene un directorio de código firmado. El directorio de código contiene hashes de las páginas de código; el kernel entiende hasta cierto punto esta estructura de datos, y dyld puede vincularla cuando se carga un binario o una biblioteca. XNU verifica la firma del directorio de código, y el subsistema VMM calcula el hash de las páginas de código cuando se cargan al ser necesarias, para comprobar que coincida con los hashes firmados del directorio. Por eso, el hash del directorio de código puede funcionar como un identificador único para cualquier programa dentro del ecosistema de Apple. Hay un bug aquí: como ese vínculo cuelga de la estructura Mach vnode, si sobrescribes un binario firmado y luego lo ejecutas, aunque la firma del nuevo archivo sea válida, el kernel se enoja y mata el proceso. Para que reconozca la nueva situación hay que reemplazar realmente todo el archivo
    Sobre esa base, Apple agrega los requisitos de código. Son programas escritos en un pequeño lenguaje de expresiones que representa restricciones sobre distintas propiedades de la firma de código. Se pueden escribir requisitos como “este binario debe estar firmado por Apple”, “se permite cualquier versión del binario firmada por un sujeto con identidad X según la autoridad certificadora Y”, “este binario debe tener el cdhash Z”, es decir, debe ser exactamente ese binario. Un binario también puede exponer un requisito designado que indica con qué requisito quiere que otras entidades lo identifiquen. Al principio parece excesivo, pero permite que un programa conserve una identidad estable e imposible de falsificar aunque evolucione
    El kernel expone la identidad de firma de una tarea a otras tareas mediante puertos. Una biblioteca de espacio de usuario puede interpretar el lenguaje de restricciones y aplicar requisitos sobre ese puerto. Por ejemplo, cuando un programa guarda una clave en el llavero del sistema, el daemon keychain implementado en espacio de usuario inspecciona el requisito designado del programa que envió el RPC y luego comprueba que coincida con futuras solicitudes de uso de la clave
    Este sistema se abstrae mediante derechos (entitlements). Los derechos son pares key=value que expresan permisos. Como es un sistema abierto, una app también puede definir sus propios derechos, aunque la mayoría los define Apple. Algunos son puramente opt-in: basta con solicitarlos y el OS los concede automáticamente y en silencio. Al principio parecen inútiles, pero permiten que la App Store describa de antemano lo que hará una app y, de forma más general, habilitan una postura de privilegios mínimos que impide que una app acceda a cosas que no necesita. Otros requieren evidencia adicional, como un perfil de aprovisionamiento. Es una estructura de datos CMS firmada que proporciona Apple y que, en términos generales, significa “la app con el requisito designado X puede usar el derecho restringido Y”, por lo que para usarlo hay que obtener permiso de Apple. Otros, además, se abusan en la práctica como una especie de sistema general de flags de firma y no tienen relación con la seguridad
    El sistema se amplía aún más mediante la cooperación entre el espacio de usuario y XNU. Poder firmar binarios es solo el comienzo; muchos programas también tienen archivos de datos. Aquí el sistema de seguridad de Apple parece un poco emparchado. El kernel no participa en la verificación de integridad de los archivos de datos. En cambio, se coloca un plist en una ubicación especial de una estructura de directorios de bundle algo arbitraria; ese plist contiene, archivo por archivo, los hashes de todos los archivos de datos del bundle; el hash del plist se incluye en la firma de código; y finalmente Gatekeeper revisa todo en la primera ejecución. El kernel le pregunta a Gatekeeper si debe permitir la ejecución del programa, y Gatekeeper decide en función de la existencia de un atributo extendido adjunto al archivo, que propagan herramientas GUI como navegadores web o descompresores. Código del OS en espacio de usuario, como Finder, hace que se invoque a Gatekeeper para revisar un programa cuando se descargó por primera vez, y Gatekeeper calcula el hash de todos los archivos dentro del bundle para comprobar que coincidan con lo firmado en el binario. Por eso en macOS aparece el lento cuadro de diálogo “Verifying app” al ejecutar algo por primera vez. Parece una forma de evitar que se congelen las apps que abren archivos de datos grandes sin usar mmap, pero es una pena porque en redes rápidas la verificación no optimizada de Gatekeeper puede ser más lenta que la descarga misma. Como Apple considera la distribución fuera de la tienda una tecnología heredada, parece no preocuparle demasiado
    Por último está Seatbelt. Es un lenguaje de programación basado en Lisp para expresar reglas de sandbox. Estos archivos se compilan en espacio de usuario a algún tipo de bytecode que evalúa el kernel. El lenguaje es bastante sofisticado y permite expresar reglas arbitrarias sobre cómo interactúan varios componentes del sistema y qué pueden hacer, todo basado en identidades de firma de código
    El esquema anterior tenía un agujero evidente que recién se cerró en versiones recientes: los archivos de datos pueden contener código y se revisaban una sola vez. En apps Electron o JVM esto es efectivamente así, porque contienen código en un formato portable. Por lo tanto, una app podía modificar archivos de datos para inyectar código en otra app y saltarse la firma de código. En macOS moderno, para evitarlo, Seatbelt somete a sandbox a todas las apps en ejecución. Hasta donde sé, en el macOS actual no hay código fuera del sandbox. Una de las políticas de sandbox impide que una app modifique sin permiso los archivos de datos de otra app. La política es bastante sofisticada: las apps firmadas por la misma entidad legal verificada por Apple pueden modificarse entre sí; una app también puede permitir modificaciones de otra app que cumpla un requisito de código; y el usuario puede otorgar permisos cuando sea necesario. Para comprobarlo, en Settings -> Privacy & Security -> App Management desactiva el permiso de Terminal.app, reinicia y luego ejecuta un comando como vim /Applications/Google Chrome.app/Contents/Info.plist. Aunque los permisos del archivo sean rw, vim lo ve como de solo lectura
    A partir de aquí termina mi comprensión, porque no trabajo en Apple. Tengo entendido que el kernel no entiende los bundles de apps, y tampoco estoy seguro de cómo decide convertir una llamada al sistema open() en solo lectura. Mi conjetura es que la política predeterminada de Seatbelt hace que el kernel realice un upcall a un daemon de seguridad que sabe leer el formato de bundle y la base de datos SQLite de permisos, y que ese daemon compara el requisito designado de quien abre el archivo con el bundle y la política expresada por el sandbox para tomar la decisión

  • No creo que seguridad sea un nombre adecuado para una función así
    En mi opinión, la seguridad siempre debería referirse a la protección del dueño o usuario de la computadora
    Estas funciones de Apple pueden usarse para mejorar la seguridad, pero su objetivo principal de diseño es darle al proveedor que vendió la computadora un control más fuerte sobre cómo el supuesto dueño usa un dispositivo que ya no debería pertenecerle. Es decir, van en la dirección de permitir que Apple decida qué programas puede ejecutar el usuario final