2 pontos por GN⁺ 2025-04-06 | 1 comentários | Compartilhar no WhatsApp
  • O Darwin da Apple é a base de tipo Unix do macOS, iOS e dos sistemas operacionais modernos da Apple, e o XNU é um kernel híbrido que combina Mach e BSD em um único kernel
  • O XNU mantém tarefas, threads, memória virtual e IPC baseado em portas do Mach, enquanto coloca os serviços BSD no mesmo espaço de endereçamento do kernel, reduzindo o custo de passagem de mensagens de um microkernel puro
  • A linhagem do NeXTSTEP com Mach 2.5+4.3BSD levou ao Mac OS X e ao Darwin após a aquisição da NeXT pela Apple em 1996, e depois recebeu gradualmente código do FreeBSD, I/O Kit, 64 bits, ARM e suporte ao Apple Silicon
  • macOS e iOS expandiram recursos como sandbox, assinatura de código, SIP, APFS, DriverKit, escalonamento com QoS, Jetsam e memória comprimida por meio da cooperação entre kernel e espaço do usuário
  • A evolução do XNU se aproxima mais de manter a base Mach/BSD do que reescrever o kernel, integrando no kernel o que exige desempenho e separando via IPC e espaço do usuário o que exige isolamento

Ponto de partida do Darwin e do XNU

  • O Darwin é o sistema operacional central de tipo Unix que sustenta o macOS, o iOS e as plataformas modernas de sistema operacional da Apple
  • No centro está o kernel XNU, sigla para “X is Not Unix”, que combina um núcleo de microkernel Mach com componentes Unix BSD
  • Essa estrutura busca equilibrar modularidade e desempenho ao aproveitar ao mesmo tempo o design baseado em passagem de mensagens do Mach e a estabilidade e compatibilidade POSIX do BSD

História: de Mach e NeXTSTEP ao Mac OS X

  • O Mach começou como um projeto liderado por Richard Rashid e Avie Tevanian na Carnegie Mellon University em 1985
    • Era um projeto de microkernel que pretendia manter no kernel apenas funções de baixo nível, como gerenciamento de memória, escalonamento de CPU e IPC, deixando sistema de arquivos, rede e drivers em servidores no espaço do usuário
    • Conceitos como tarefas, threads, portas Mach, copy-on-write e objetos de memória se consolidaram como objetos centrais do kernel
  • O NeXTSTEP foi lançado em 1989, colocando um subsistema Unix 4.3BSD sobre o kernel Mach 2.5
    • A NeXT priorizou desempenho e escolheu integrar o código BSD ao espaço de endereçamento do kernel em vez de seguir um modelo de microkernel puro
    • Também incluía o DriverKit baseado em Objective-C, que depois levou à linhagem do XNU da Apple
  • Em 1996, a Apple adquiriu a NeXT e escolheu o NeXTSTEP como base do novo Mac OS X
    • O projeto Rhapsody começou, trazendo para a Apple o kernel híbrido Mach/BSD da NeXT
    • Depois, o XNU incorporou código da linha Mach 3.0 baseado no OSFMK 7.3 e código do 4.4BSD e do FreeBSD

Desenvolvimento inicial do Darwin e do Mac OS X

  • Em 1999, a Apple lançou a Developer Preview do Mac OS X e, em 2000, publicou o Darwin 1.0, abrindo para desenvolvedores o kernel XNU e o espaço de usuário Unix básico
  • O Mac OS X 10.0 Cheetah foi lançado comercialmente em 2001 com base no Darwin 1.3.1
  • O foco das mudanças iniciais estava em reforçar a camada BSD, a rede, o sistema de arquivos e o desempenho de threads
    • O Mac OS X 10.1 Puma melhorou o desempenho do gerenciamento de threads e o suporte a threads em tempo real
    • O Mac OS X 10.2 Jaguar incluiu IPv6, IPSec, mDNSResponder e journaling no HFS+
    • O Mac OS X 10.3 Panther integrou melhorias do kernel do FreeBSD 5 e travas de kernel mais granulares, fortalecendo o uso de multiprocessadores
  • O Mac OS X 10.4 Tiger recebeu certificação UNIX 03, adotou kqueue/kevent do FreeBSD e manteve uma base multiplataforma para a transição aos Macs com Intel

64 bits e as demandas móveis trazidas pelo iPhone OS

  • O Mac OS X 10.5 Leopard foi baseado no Darwin 9 e introduziu execução de kernel em 64 bits, drivers de 64 bits, ASLR, sandbox e DTrace
  • Em 2007, o primeiro iPhone OS também foi lançado com base no Darwin 9, expandindo o XNU para dispositivos móveis ARM
    • Como os primeiros iPhones tinham RAM limitada e não podiam usar swap, eles usavam o mecanismo Jetsam para encerrar apps em segundo plano em situações de pouca memória
    • O iPhone OS executava apps de terceiros dentro de sandbox e exigia assinatura de código rigorosa nos binários
  • O Mac OS X 10.6 Snow Leopard encerrou o suporte a PowerPC e reforçou otimizações de 64 bits e multicore centradas em Intel
    • O Grand Central Dispatch e o libdispatch são bibliotecas de espaço do usuário, mas aproveitam o suporte do kernel a pools de threads e escalonamento
    • O OpenCL também exigia integração estreita entre frameworks de espaço do usuário e drivers de kernel para computação em GPU
  • O iOS 4 ajustou o escalonador para diferenciar prioridades de apps em segundo plano e dar suporte a SoCs ARM multicore

Expansão dos recursos de kernel no macOS e iOS modernos

  • O OS X 10.9 Mavericks adicionou memória comprimida e coalescência de temporizadores
    • A memória comprimida reduz o swap em disco ao comprimir páginas inativas dentro da própria RAM
    • A coalescência de temporizadores reduz o consumo de energia ao alinhar os momentos de ativação da CPU
  • O OS X 10.11 El Capitan introduziu o System Integrity Protection, ou SIP
    • O SIP é imposto pelo kernel por meio da estrutura de Mandatory Access Control da camada BSD, impedindo que até processos root alterem arquivos e processos críticos do sistema
  • O macOS 10.13 High Sierra adotou o APFS como sistema de arquivos padrão
    • A camada VFS do XNU foi expandida para dar suporte a snapshots, clonagem e criptografia em nível de contêiner do APFS
    • No mesmo período, o carregamento de kexts de terceiros passou a exigir aprovação do usuário
  • O macOS 10.15 Catalina introduziu o DriverKit moderno
    • O DriverKit move muitos drivers para Driver Extensions no espaço do usuário, fora do kernel
    • O kernel fornece acesso limitado ao hardware para drivers em espaço do usuário via IPC e memória compartilhada
    • O Catalina também introduziu um volume de sistema somente leitura, reforçando a proteção do SIP

O XNU na era do Apple Silicon

  • O macOS 11 Big Sur e o Darwin 20, em 2020, foram a primeira versão a oferecer suporte a Macs com Apple Silicon
  • O XNU já suportava ARM por causa do iOS, mas nos Macs com Apple Silicon também precisava considerar a arquitetura de CPU heterogênea big.LITTLE
    • O escalonador reconhece núcleos heterogêneos para colocar threads pesadas e de alta prioridade em núcleos de desempenho, e threads de baixo QoS ou em segundo plano em núcleos de eficiência
    • As classes de QoS podem servir como dicas de escalonamento que influenciam a escolha do tipo de núcleo no Apple Silicon
  • Na arquitetura de memória unificada do Apple Silicon, o gerenciador de memória do kernel e os drivers de GPU cuidam do compartilhamento de buffers
    • A abstração de VM do Mach se encaixa no compartilhamento de objetos de memória entre espaço do usuário e GPU por remapeamento de VM, em vez de cópia
  • O backend ARM64 suporta Pointer Authentication, usando chaves PAC em frames de exceção e ponteiros de sistema, contribuindo para mitigar ataques ROP
  • O XNU continua sendo a base comum de várias plataformas Apple, como macOS, iOS, watchOS, tvOS, bridgeOS e visionOS

Estrutura de kernel híbrido do XNU

  • Os componentes Mach e BSD do XNU são vinculados em um único binário de kernel e compartilham o mesmo espaço de endereçamento
    • Não há fronteira de proteção entre Mach e BSD, e dentro do kernel eles interagem por chamadas de função normais, não por mensagens IPC
    • Uma system call Unix como read() não envia mensagem a um servidor BSD separado; ela entra diretamente no código do sistema de arquivos BSD dentro do kernel
  • O Mach cuida da infraestrutura central do kernel
    • Gerencia criação e término de tarefas e threads, troca de contexto, escalonamento de baixo nível, locks, temporizadores e filas de escalonamento
    • Cada processo BSD corresponde a uma tarefa Mach, e cada thread corresponde a uma thread Mach
    • A VM do Mach fornece mapas de endereço virtual, objetos de memória, copy-on-write e compartilhamento de memória baseado em IPC
  • O BSD fornece a natureza e os serviços Unix
    • Gerencia PID, ID de usuário, sinais, threads POSIX, sistemas de arquivos, rede, IPC Unix, I/O de dispositivos, permissões e frameworks de segurança
    • O VFS lida com sistemas de arquivos como HFS+, APFS e NFS, e no caso de arquivos mapeados em memória se conecta à VM do Mach por meio do vnode pager
    • Sandbox e SIP funcionam pela cooperação entre módulos de segurança BSD e restrições nas portas de tarefa do Mach
  • O I/O Kit é o terceiro eixo do XNU, um framework de drivers orientado a objetos escrito em uma forma restrita de C++
    • Ele representa dispositivos e drivers como uma hierarquia de classes, e os drivers rodam dentro do kernel como objetos C++
    • Para o espaço do usuário, fornece acesso limitado por meio de propriedades do I/O Registry e da interface user client
    • Até a chegada do DriverKit no macOS moderno, a maioria dos drivers funcionava dentro do kernel na forma de kexts

IPC do Mach e serviços do sistema

  • O XNU não usa mensagens Mach no caminho das system calls Unix, mas faz amplo uso de Mach IPC para comunicação entre serviços em espaço do usuário e entre kernel e processos
  • As portas Mach são usadas como handles em espaço do usuário para vários objetos do kernel
    • Cada tarefa tem uma porta de tarefa, e processos com privilégio podem usá-la para inspecionar ou controlar outras tarefas
  • Eventos e notificações também são entregues por mensagens Mach
    • O WindowServer recebe eventos de entrada do usuário do kernel por mensagens Mach
    • O Grand Central Dispatch usa internamente portas Mach para suspender threads de espera por eventos
    • kqueue/kevent pode aguardar ao mesmo tempo mensagens de portas Mach e descritores de arquivo
  • O framework XPC da Apple é construído sobre mensagens Mach
    • Conexões XPC são internamente baseadas em portas Mach
    • O modelo de permissões das portas Mach é usado para verificar os privilégios do chamador em serviços como o securityd do Keychain
    • Mensagens Mach podem transportar memória out-of-line e direitos sobre portas, sendo usadas na composição de RPCs de alto nível
  • O MIG, ou Mach Interface Generator, é usado para gerar definições de interface e código de envio e recebimento de mensagens entre kernel e espaço do usuário

Escalonador e gerenciamento de threads

  • O escalonador do XNU começou a partir do escalonador round-robin baseado em prioridade do Mach, mas foi fortemente modificado para atender às demandas de desktop e mobile
  • Historicamente, o Mach definia prioridades de thread na faixa de 0 a 127, e o XNU usa valores como sched_pri e base_pri
    • Threads time-sharing podem ter a prioridade alterada conforme o uso
    • Threads em tempo real usam prioridade fixa
  • O XNU lida com eficiência e balanceamento de carga por meio de filas de execução por CPU e interrupções do escalonador
  • O sandbox de apps no iOS e a execução em segundo plano refletem no escalonador conceitos de função de trabalho ou grupos de prioridade
  • As classes de QoS foram integradas ao escalonamento desde o iOS 8 e o OS X 10.10
    • Classes como user-interactive, user-initiated, default, utility e background afetam faixas de prioridade e o escalonamento
    • Threads criadas com Grand Central Dispatch ou NSThread herdam QoS
    • No Apple Silicon, threads com QoS de background podem ser colocadas em núcleos de eficiência
  • Também há suporte a filas em tempo real e escalonamento baseado em deadline para áudio em tempo real e tarefas críticas

Gerenciamento de memória e Mach VM

  • O gerenciamento de memória do XNU é centrado no subsistema Mach VM
  • Cada tarefa Mach tem um espaço de endereçamento virtual representado por VM map e VM region
    • fork() usa copy-on-write em vez de copiar imediatamente toda a memória
    • Pai e filho compartilham as mesmas páginas até que haja escrita
  • O Mach usa os conceitos de objetos de memória e pager
    • Para memória anônima, o pager padrão é o daemon dynamic_pager no espaço do usuário, que gerencia arquivos de swap quando necessário
    • A memória de arquivos é tratada pelo vnode pager na camada BSD dentro do kernel, em interação com o código do sistema de arquivos
  • A memória comprimida do Mavericks foi implementada com a adição de um compression pager dentro do kernel
    • Quando a pressão de memória aumenta, páginas inativas são comprimidas e armazenadas no compressor pool na RAM em vez de irem diretamente para o disco
    • O swap em disco é usado quando a compressão não basta
  • O gerenciamento da memória física é responsabilidade do pmap, a camada dependente de arquitetura
    • O pmap gerencia tabelas de páginas ou estruturas equivalentes da arquitetura correspondente
    • No ARM64, recursos de segurança e questões relacionadas a cache também se conectam ao pmap
  • O shared cache do dyld usa com eficiência as mesmas páginas físicas mapeando-as como somente leitura em vários processos

Suporte à virtualização

  • Nos Macs com Intel, o Hypervisor.framework foi oferecido desde o OS X 10.10 para virtualização em espaço do usuário
    • Ele usa Intel VT-x para permitir que um processo em espaço do usuário funcione como um monitor de máquina virtual
    • Ferramentas como xhyve e alguns apps de virtualização aproveitam esse recurso
  • No Apple Silicon, o Virtualization.framework do macOS 11 roda sobre um hipervisor no kernel para ARM64
    • Desenvolvedores podem executar VMs Linux ou macOS a partir do espaço do usuário
    • Em vez de permitir um hipervisor arbitrário de terceiros dentro do kernel, a Apple adota acesso via seus próprios frameworks e permissões
  • Da perspectiva do kernel, os recursos do hipervisor incluem gerenciamento de memória física do guest, trap-and-emulate de instruções sensíveis e exposição de interfaces de vCPU
  • Do ponto de vista do host, o escalonador Mach agenda as vCPUs como threads, e o subsistema de memória é usado para mapear a memória do guest
  • No iOS, a virtualização também pode ser possível sob condições e permissões específicas, e houve casos de execução de VMs Linux ao ativar o hipervisor em dispositivos A14 com jailbreak

Secure Enclave e Exclaves

  • O macOS usa dois mecanismos de isolamento, Secure Enclave e exclaves, para proteger operações e dados sensíveis
  • A Secure Enclave é um subsistema dedicado e reforçado integrado aos SoCs da Apple
    • Está presente em iPhones, iPads, Macs com T2 ou Apple Silicon e outros dispositivos
    • Executa seu próprio sistema operacional baseado em microkernel e gerencia informações sensíveis, como chaves criptográficas e dados biométricos
    • Seu objetivo é isolar dados críticos mesmo que o processador principal de aplicações ou o kernel sejam comprometidos
  • Os Exclaves são uma estrutura de segurança mais nova, surgida no macOS 14.4 e no iOS 17
    • Em vez de manter operações sensíveis no mesmo domínio de privilégio do kernel principal XNU, alguns recursos centrais são separados em um domínio “externally located”
    • Entre os recursos visados estão serviços de Apple ID, buffers de áudio, dados de sensores e componentes de controle de indicadores
    • kexts especiais e frameworks privados, como ExclaveKextClient.kext, ExclaveSEPManagerProxy.kext e ExclavesAudioKext.kext, participam desse gerenciamento
  • Essa separação fornece uma camada extra de defesa ao isolar operações dentro dos exclaves mesmo se o kernel principal for comprometido

Direção de design de longo prazo

  • O Darwin e o XNU seguem um design misto, nem totalmente microkernel nem totalmente monolítico
  • O núcleo baseado em Mach ajudou a adaptar o sistema a novas arquiteturas e novos recursos, enquanto a camada BSD fornece um ambiente compatível com POSIX e ferramentas e APIs Unix
  • A Apple acomodou transições de CPU de PowerPC para Intel e ARM, além de novas categorias de dispositivos como iPhone, Apple Watch e Apple Vision Pro, com base no XNU
  • As mudanças no kernel avançam principalmente de três formas
    • Novos recursos são estendidos sobre o kernel existente
    • Componentes críticos para desempenho são integrados ao kernel
    • Componentes que exigem isolamento são separados via Mach IPC e espaço do usuário
  • As versões de código aberto do Darwin oferecem aos pesquisadores uma janela para estudar um kernel híbrido comercial, embora com limitações no escopo do que é publicado

1 comentários

 
GN⁺ 2025-04-06
Opiniões no Hacker News
  • O sistema de memória virtual do Mach entrou não só no 4.4BSD e no FreeBSD, mas também no NetBSD[0] e no OpenBSD[1], mas aparentemente não no 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 preciso, não é bem assim. 386BSD, FreeBSD e NetBSD herdaram inicialmente um design no estilo do Mach 2.5, mas o FreeBSD substituiu bem rapidamente todos os vestígios restantes da VM do Mach[0] por uma reescrita de VM moderna e de bom desempenho
      Na época do FreeBSD 4, já não havia código Mach original na base de código do kernel; isso já tinha sido concluído no fim dos anos 1990, então a ligação que se pode fazer entre o FreeBSD e o Mach se limita, no máximo, às fases muito iniciais de derivação/base
      NetBSD e OpenBSD também seguiram por algum tempo, mas esbarraram nos limites de desempenho, SMP/escalabilidade e rede do design do Mach, e fizeram uma reescrita completa para o UVM (Unified Virtual Memory), projetado e liderado por Chuck Cranor; o OpenBSD depois tomou essa implementação emprestada e ainda a usa hoje
      Entre os BSDs vivos[1], o único que continua usando Mach é o XNU/Darwin, e mesmo assim não é Mach 2.5, mas Mach 3. Houve Mach 2.5, 3 e 4 (o GNU/Hurd usa Mach 4), mas a compatibilidade entre eles é baixa e eles compartilham principalmente influência no nível da arquitetura geral; portanto, é melhor vê-los como designs distintos com influências em comum
      [0] Para começo de conversa, nem havia tantos vestígios assim
      [1] Não tenho muita certeza se o DragonBSD está morto ou vivo hoje
  • No Darwin, é interessante a velocidade com que os componentes centrais mudam radicalmente. Da renúncia à compatibilidade retroativa de chamadas de sistema à assinatura de código obrigatória e ao dyld_shared_cache, que eliminou arquivos individuais de bibliotecas do sistema para acelerar o carregamento de executáveis dinâmicos, é uma forma de projetar orientada a resultados, sem nostalgia nem vacas sagradas
    Parece uma abordagem que só uma grande fabricante de hardware como a Apple consegue executar

  • O texto diz que o daemon pager, que gerencia arquivos de swap, roda em espaço de usuário, e que a memória do kernel também pode ser enviada para swap, mas não explica como um daemon em espaço de usuário faz swap da memória do kernel
    Fico curioso se há uma exceção hardcoded para um daemon especial, ou se ele usa uma chamada de sistema especial. Onde dá para ver mais detalhes sobre o gerenciamento de memória em espaço de usuário?

    • Essa explicação é imprecisa e mistura várias coisas. O microkernel Mach originalmente suportava paginação real em espaço de usuário, parecida com mmap, na qual se colocava um daemon arbitrário no lugar do sistema de arquivos; a interface pode ser vista aqui:
      https://web.mit.edu/darwin/src/modules/xnu/osfmk/man/memory_...
      Porém, não está claro se o Darwin chegou a usar esse recurso de fato e, pelo menos nos últimos cerca de 20 anos, não usou. O dynamic_pager nunca usou essa interface; quando o XNU avisava que havia falta de swap, ele criava arquivos de swap e usava uma interface Mach muito mais limitada, passando-os ao kernel pelas chamadas de sistema macx_swapon e macx_swapoff. O swap de fato era feito pelo kernel, e o código antigo do dynamic_pager está aqui:
      https://github.com/apple-oss-distributions/system_cmds/blob/...
      Essa funcionalidade agora também foi para dentro do kernel, então o dynamic_pager atual praticamente não faz quase nada:
      https://github.com/apple-oss-distributions/system_cmds/blob/...
      A maior parte da memória do kernel é fixa (wired) e não pode ser paginada, mas o kernel pode solicitar explicitamente memória paginável por meios como IOMallocPageable, e essa memória pode ser enviada para swap em disco. Porém, isso é pouco usado, e esse tipo de código precisa tomar cuidado para evitar deadlocks. Mesmo que o espaço de usuário não participe mais da “paginação” em si, é comum haver intervenção do espaço de usuário uma ou duas camadas abaixo, como em sistemas de arquivos em espaço de usuário baseados em FSKit ou FUSE, sistemas de arquivos sobre imagens de disco, ou NFS/SMB passando por extensões de rede em espaço de usuário. Mas posso estar errado sobre essa última parte. Sistemas de arquivos que bloqueiam no espaço de usuário certamente são possíveis, mas pode ser que não haja suporte para colocar swap sobre esse tipo de sistema de arquivos
    • https://github.com/apple-oss-distributions/xnu
  • Sempre que vejo discussões sobre o kernel Darwin, fico pensando em como teria sido diferente se a Apple simplesmente tivesse feito um fork do Linux e colocado os serviços do sistema operacional por cima dele.
    Especialmente vendo o quanto a Apple se apega ao Darwin, fica uma impressão ruim, porque parece haver um desequilíbrio entre o que o open source perdeu e o retorno sobre o tempo e o custo que a Apple precisa investir.

    • A Apple nunca teve um momento adequado para fazer essa transição. O NeXTSTEP veio antes do Linux, e, ao migrar para o Mac OS X, a Apple não teria condições de assumir, além de todo o resto, um projeto de substituição completa do kernel.
      O Linux do fim dos anos 1990 também não era uma escolha claramente melhor; depois de algumas versões do OS X, quando ele já havia se consolidado como o sistema operacional tipo UNIX mais bem-sucedido em PCs de consumo, mudar para uma base Linux teria trazido pouco benefício de curto prazo e muito custo e risco.
      Se a Apple tivesse arrastado o MacOS clássico por mais cinco anos, ou se o Linux tivesse amadurecido cinco anos antes, a transição para o OS X poderia ter sido muito diferente. Mas abandonar o XNU por um kernel Linux anterior ao 2.6 não fazia sentido.
    • Na época em que a Apple comprou a NeXT, o Linux ainda estava em pleno desenvolvimento e não estava bem estabelecido. Como o Linux era um kernel monolítico, também não oferecia o nível de compartimentalização que o Mach fornecia.
      Pelos critérios atuais, o FreeBSD reúne muitas das vantagens do Darwin com muito do caráter open source ao estilo Linux. Se você quer um ambiente mais seguro sem a dependência cada vez maior da Apple, FreeBSD e outros BSDs também valem ser considerados como alvos de distribuição.
    • Curiosamente, a Apple contribuiu para portar o Linux para Macs PowerPC no projeto MkLinux, iniciado em 1996. Isso veio antes da aquisição da NeXT no fim daquele mesmo ano:
      https://en.m.wikipedia.org/wiki/MkLinux
      Parece que não houve trabalho para levar a GUI do Macintosh e o ecossistema de aplicativos para o Linux. Mas, mesmo antes da aquisição da NeXT, a Apple já executava o ambiente Macintosh sobre Unix com o A/UX para Macs 68k e, depois, com o Macintosh Application Environment para Solaris e HP-UX; este último executava o Mac OS como um processo Unix. Se minha memória não falha, o trabalho no Macintosh Application Environment serviu de base para o Blue Box do Rhapsody e, mais tarde, para o ambiente Classic do Mac OS X. Em teoria, também daria para imaginar um porte do Macintosh Application Environment para o MkLinux. Em 1996, após o acordo nos processos envolvendo BSD, os BSDs livres e open source modernos já existiam.
      É claro que, em meados dos anos 1990, executar o Mac OS clássico como um processo sobre um SO moderno como Linux, FreeBSD, BeOS ou Windows NT não era uma estratégia realista para desktops de consumo. Eram necessários recursos de nível workstation, enquanto a Apple ainda dava suporte a Macs 68k, e o Mac OS 8 também rodava em algumas máquinas 68030/68040. Na era G3/G4 isso teria sido mais realista e, nos anos 2000, talvez fosse possível executar cada programa clássico de Macintosh como um processo Mac OS separado sobre um SO moderno, mas, sem o retorno de Jobs, a Apple provavelmente não teria passado de 1998. Além disso, a aquisição da NeXT também levou ao Mac o Cocoa, o IOKit, o Quartz (sucessor do Display PostScript) e outras tecnologias que hoje são centrais.
    • Não sei por que deveríamos querer uma monocultura ainda pior. Já colocamos ovos demais em uma única cesta. Espero que haja mais diversidade entre kernels, não mais unificação.
      Visto de outro ângulo, isso me soa parecido com a proposta de que a Apple deveria migrar o Safari para cima do Chromium.
    • O XNU é apenas parcialmente open source. O núcleo está aberto, mas partes importantes, como o sistema de arquivos APFS, ficam de fora.
      Se tivesse feito um fork do Linux, talvez a Apple fosse legalmente obrigada a abrir todos os módulos do kernel como open source. Isso provavelmente seria positivo para a humanidade, mas não teria sido a direção que a Apple queria seguir.
  • Este texto tem muito carinho e muito trabalho por trás. Como alguém que viveu boa parte dessa história, já portou código do NeXTSTEP para Windows, investigou as tentativas de recriação do GNUStep, se lembra do YellowBox e do OpenStep, leu livros sobre a arquitetura interna e acompanha o conteúdo da WWDC com constância, ele bate quase exatamente com minha memória de como os diversos sistemas evoluíram.

  • Jobs tentou recrutar Torvalds para trabalhar no Mac OS X, e Linus recusou: https://www.macrumors.com/2012/03/22/steve-jobs-tried-to-hir...

    • Logo um microkernel sendo trabalhado por Torvalds é algo difícil de imaginar.
  • Não sei se o fato de o I/O Kit ter sido escrito nesse subconjunto de C++ foi apenas por velocidade. Na época houve controvérsia. Isso porque, quando a Apple anunciou o MacOS X, disse que ele não seria compatível com o software existente e que todos os parceiros teriam de reescrever tudo em Objective-C.
    Como a reação não foi boa, a Apple recuou e introduziu o Carbon, uma camada de API para aplicações em C++, e o Core Foundation, que serve de base para o Foundation baseado em Objective-C. É também por isso que existe Obj-C++. O ponto interessante foi tornar o gerenciamento de memória “toll-free bridged”. Ou seja, objetos alocados no mundo C/C++ podem ser passados para Obj-C sem overhead adicional.

    • O C++ do IOKit roda no kernel, então não tem muita relação com as tecnologias mencionadas. Todas elas são tecnologias exclusivas do espaço de usuário.
  • A Apple deveria ter cultivado uma comunidade livre e open source melhor em torno do XNU. Mesmo agora que migrou para ARM, deveria ter existido uma distribuição capaz de rodar em x64.

  • Eu queria entender o Darwin com esse nível de profundidade, e foi um bom texto

    • Mac OS X Internals, do Singh, é um dos meus livros favoritos. É uma excelente análise que cobre com bastante profundidade a época do Mac OS X 10.4, então eu realmente gostaria que existisse uma versão atualizada
      Ele também é citado no fim deste texto. É um material que vai permanecer por muito tempo na história do macOS
    • Eu também queria entender o Windows NT nesse nível de profundidade. Algo que pulasse a discussão sobre Win32 e tratasse do que há por baixo. Pelo que entendo, Win32 é apenas uma personalidade, e também existiram o Windows Services for UNIX na época do Windows XP e o Subsystem for UNIX-based Applications no Windows Vista
      O kernel NT subjacente é flexível o bastante para permitir conformidade com POSIX, então um texto abordando esse tipo de conteúdo seria interessante
  • É uma boa recapitulação histórica, mas acaba pulando muito do excelente trabalho de segurança que diferencia os sistemas operacionais da Apple do Linux ou do Windows. Parece que a Apple não recebe a devida avaliação pelo quão à frente está hoje em termos de segurança. Talvez, algum dia, essa percepção cresça a ponto de pessoas que trabalham em ambientes sensíveis serem obrigadas pelo CISO a usar Mac
    O ponto central é o sistema de assinatura de código. Ele permite conceder permissões a apps ou confiná-los em um sandbox, e garante que essa imposição seja de fato mantida. A Apple não usa ELF como a maioria dos UNIX; usa um formato chamado Mach-O. A maioria das diferenças entre ELF e Mach-O não importa, mas o importante é que o Mach-O dá suporte a uma seção adicional que contém um diretório de código assinado. O diretório de código contém hashes das páginas de código; o kernel entende essa estrutura de dados em certa medida, e o dyld consegue vinculá-la quando um binário ou biblioteca é carregado. O XNU verifica a assinatura do diretório de código, e o subsistema de VMM, quando páginas de código são carregadas sob demanda, calcula seus hashes e valida se correspondem aos hashes assinados no diretório. Assim, o hash do diretório de código pode funcionar como um identificador único para qualquer programa dentro do ecossistema da Apple. Há um bug nisso: como essa associação fica pendurada na estrutura Mach vnode, se você sobrescrever um binário assinado e executá-lo, mesmo que a assinatura do novo arquivo seja válida, o kernel fica irritado e mata o processo. Para que ele reconheça a nova situação, é preciso substituir de fato o arquivo inteiro
    Sobre essa base, a Apple adiciona os requisitos de código. Eles são programas escritos em uma pequena linguagem de expressões que descreve restrições sobre vários atributos da assinatura de código. É possível escrever requisitos como “este binário deve ser assinado pela Apple”, “permita qualquer versão de um binário assinado por uma entidade com identidade X segundo a autoridade certificadora Y”, “este binário deve ter o cdhash Z”, ou seja, deve ser exatamente aquele binário. Um binário também pode expor um requisito designado, indicando a outras partes por qual requisito ele deseja ser identificado. À primeira vista parece exagero, mas isso permite que um programa mantenha uma identidade estável e impossível de falsificar mesmo conforme evolui
    O kernel expõe a identidade de assinatura de uma tarefa a outras tarefas por meio de ports. Uma biblioteca em espaço de usuário interpreta a linguagem de restrições e pode aplicar requisitos a esse port. Por exemplo, quando um programa armazena uma chave no keychain do sistema, o daemon do keychain, implementado em espaço de usuário, examina o requisito designado do programa que enviou o RPC e depois verifica se ele corresponde às solicitações futuras de uso da chave
    Esse sistema é abstraído por entitlements. Entitlements são pares key=value que expressam autorizações. Como é um sistema aberto, um app também pode definir seus próprios entitlements, mas a maioria é definida pela Apple. Alguns são puramente opt-in: basta solicitá-los e o OS os concede automaticamente e em silêncio. No começo isso parece inútil, mas permite que a App Store descreva de antemão o que o app fará e, de forma mais geral, viabiliza uma postura de privilégio mínimo, em que o app não acessa aquilo de que não precisa. Alguns exigem evidências adicionais, como um provisioning profile. Trata-se de uma estrutura CMS assinada fornecida pela Apple, que significa, em linhas gerais, “um app com o requisito designado X pode usar o entitlement restrito Y”; portanto, para usá-lo é preciso obter permissão da Apple. Outros, ainda, são basicamente abusados como um sistema genérico de flags de assinatura e não têm relação com segurança
    O sistema se expande ainda mais pela cooperação entre o espaço de usuário e o XNU. Poder assinar binários é apenas o começo; muitos programas também têm arquivos de dados. Aqui, o sistema de segurança da Apple parece um tanto remendado. O kernel não participa da verificação de integridade dos arquivos de dados. Em vez disso, há um plist em um local especial de uma estrutura de diretórios de bundle um pouco arbitrária; esse plist contém, arquivo por arquivo, os hashes de todos os arquivos de dados dentro do bundle; o hash do plist entra na assinatura de código; e, por fim, o Gatekeeper verifica tudo na primeira execução. O kernel pergunta ao Gatekeeper se deve permitir a execução do programa, e o Gatekeeper decide com base na presença de atributos estendidos anexados ao arquivo e propagados por ferramentas GUI como navegadores web ou utilitários de descompactação. Código de espaço de usuário do OS, como o Finder, faz com que o Gatekeeper seja chamado para verificar um programa quando ele foi baixado pela primeira vez, e o Gatekeeper calcula o hash de todos os arquivos dentro do bundle para checar se correspondem ao conteúdo assinado no binário. É por isso que, no macOS, aparece a caixa de diálogo lenta “Verifying app” na primeira execução. Parece uma forma de evitar que apps que abrem arquivos de dados grandes sem usar mmap travem, mas é uma pena, porque em redes rápidas a verificação não otimizada do Gatekeeper pode ser mais lenta que o próprio download. A Apple parece não se importar muito, pois vê a distribuição fora da loja como uma tecnologia legada
    Por fim, há o Seatbelt. É uma linguagem de programação baseada em Lisp para expressar regras de sandbox. Esses arquivos são compilados no espaço de usuário para algum bytecode que o kernel avalia. A linguagem é bastante sofisticada, permitindo expressar regras arbitrárias, todas baseadas em identidades de assinatura de código, sobre como vários componentes do sistema interagem e o que podem fazer
    O esquema acima tinha uma brecha óbvia que só foi fechada em versões recentes: arquivos de dados podem conter código e são verificados apenas uma vez. Apps Electron ou JVM, por exemplo, realmente têm código em formatos portáveis. Portanto, um app podia modificar os arquivos de dados de outro app para injetar código e contornar a assinatura de código. Nas versões mais recentes do macOS, para impedir isso, o Seatbelt coloca todos os apps em execução em sandbox. Até onde sei, no macOS moderno não há código fora de sandbox. Uma das políticas de sandbox impede que um app modifique os arquivos de dados de outro app sem autorização. A política é bem sofisticada: apps assinados pela mesma pessoa jurídica verificada pela Apple podem modificar uns aos outros; um app também pode permitir modificações por outro app que satisfaça um requisito de código; e o usuário pode conceder a permissão quando necessário. Para verificar isso, vá em Settings -> Privacy & Security -> App Management, desative a permissão do Terminal.app e reinicie; depois execute um comando como vim /Applications/Google Chrome.app/Contents/Info.plist. Embora as permissões do arquivo sejam rw, o vim o enxerga como somente leitura
    A partir daqui, como não trabalho na Apple, meu entendimento também acaba. Pelo que sei, o kernel não entende bundles de apps, e também não tenho certeza de como ele decide transformar a chamada de sistema open() em somente leitura. Meu palpite é que a política padrão do Seatbelt faz o kernel realizar um upcall para um daemon de segurança que sabe ler o formato de bundle e o banco de dados SQLite de permissões; esse daemon então compara o requisito designado de quem está abrindo o arquivo com a política expressa pelo bundle e pelo sandbox, e toma a decisão

  • Não acho apropriado chamar esse tipo de recurso de segurança
    Na minha opinião, segurança deve sempre se referir à proteção do dono ou usuário do computador
    Esses recursos da Apple podem até ser usados para melhorar a segurança, mas o principal objetivo de design é dar ao fornecedor que vendeu o computador um controle maior sobre como o proprietário, em teoria, usa um dispositivo que já não deveria pertencer a esse fornecedor. Ou seja, é uma direção em que a Apple passa a poder decidir quais programas o usuário final pode executar