Zircão? O que é isso
Em agosto de 2016, sem nenhum anúncio oficial do Google, as fontes do novo sistema operacional foram descobertas
Fúcsia. Este sistema operacional é baseado em um microkernel chamado Zircon, que por sua vez é baseado no LK (Little Kernel) .
Fuchsia não é Linux
O que será discutido no artigo?
O vDSO em Zircon é o único meio de acessar chamadas do sistema (syscalls) .
É realmente impossível chamar diretamente as instruções do processador SYSENTER / SYSCALL a partir do nosso código? Não, estas instruções do processador não fazem parte da ABI do sistema. É proibido o código do usuário seguir diretamente estas instruções.
Aqueles que querem saber mais detalhes sobre essa etapa arquitetônica, convido você para o gato.

Zircon vDSO (objeto virtual compartilhado dinâmico)
O acrônimo vDSO significa Virtual dynamical S hared O bject:
- Objeto compartilhado dinâmico é um termo usado para se referir a bibliotecas compartilhadas para o formato ELF (arquivos .so).
- Este objeto é virtual porque não é carregado a partir de um arquivo separado existente no sistema de arquivos. A imagem do vDSO é fornecida diretamente pelo kernel.
Suporte ao Kernel
O suporte ao vDSO como a única ABI controlada para aplicativos no modo de usuário é implementado de duas maneiras:
Projetando um objeto de memória virtual ( VMO, Virtual Memory Object ).
Quando o zx_vmar_map processa o VMO para vDSO (e ZX_VM_PERM_EXECUTE
solicitado nos argumentos), o kernel exige que o deslocamento e o tamanho correspondam estritamente ao segmento executável do vDSO. Isso (inclusive) garante apenas uma projeção de vDSO na memória do processo. Após a primeira projeção bem-sucedida do vDSO no processo, ele não pode mais ser excluído. E uma tentativa de reprojetar o vDSO na memória do processo, tenta excluir o VMO projetado para o vDSO ou o projeto com o deslocamento e / ou tamanho incorretos falha com o erro ZX_ERR_ACCESS_DENIED
.
O deslocamento e o tamanho do código vDSO são extraídos no estágio de compilação do arquivo ELF e depois usados no código do kernel para executar as verificações acima. Após a primeira projeção vDSO bem-sucedida, o kernel do sistema operacional lembra o endereço do processo de destino para acelerar as verificações.
Verifique os endereços de retorno para obter funções de chamada do sistema.
Quando o código do modo de usuário chama o kernel, um número de chamada de sistema de baixo nível é transmitido no registro. As chamadas de sistema de baixo nível são a interface interna (privada) entre o vDSO e o núcleo de zircão. Alguns (a maioria) correspondem diretamente às chamadas do sistema da ABI pública, enquanto outros não.
Para cada chamada de sistema de baixo nível no código vDSO, há um conjunto fixo de compensações no código que faz essa chamada. O código fonte do vDSO define caracteres internos que identificam cada um desses locais. No momento da compilação, esses locais são recuperados da tabela de símbolos vDSO e usados para gerar código do kernel que determina a previsão da validade do endereço do código para cada chamada de sistema de baixo nível. Esses predicados permitem verificar rapidamente o código de chamada quanto à validade, considerando o deslocamento desde o início do segmento de código vDSO.
Se o predicado determinar que o código de chamada não pode fazer uma chamada de sistema, uma exceção sintética será lançada, semelhante à mesma que se o código de chamada tentasse executar uma instrução inexistente ou privilegiada.
vDSO ao criar um novo processo
Para iniciar a execução do primeiro encadeamento de um processo recém-criado, a chamada do sistema zx_process_start é usada . O último parâmetro desta chamada do sistema (consulte arg2 na documentação) passa o argumento para o primeiro encadeamento do processo criado. De acordo com o contrato aceito, o carregador do programa mapeia o vDSO para o espaço de endereço do novo processo (para um local aleatório selecionado pelo sistema) e transfere o endereço base do mapeamento com o argumento arg2 para o primeiro encadeamento do processo criado. Este endereço é o endereço do cabeçalho do arquivo ELF, no qual as funções nomeadas necessárias podem ser encontradas para fazer chamadas do sistema.
Cartão de memória (layout) vDSO
O vDSO é uma biblioteca compartilhada EFL comum que pode ser considerada como qualquer outra. Mas para vDSO, um pequeno subconjunto de todo o formato ELF é intencionalmente selecionado. Isso tem várias vantagens:
- O mapeamento de uma ELF para o processo é simples e não inclui nenhum caso de limite complexo necessário para dar suporte total aos programas de ELF.
- O uso do vDSO não requer ligação ELF dinâmica totalmente funcional. Em particular, o vDSO não possui realocações dinâmicas. Projetar segmentos PT_LOAD de um arquivo ELF é a única ação necessária.
- O código vDSO é sem estado e reentrante. Funciona exclusivamente com registradores de processador e a pilha. Isso o torna adequado para uso em uma ampla variedade de contextos com restrições mínimas, em conformidade com o sistema operacional ABI obrigatório. Ele também simplifica a análise e verificação de código para confiabilidade e segurança.
Toda a memória vDSO é representada por dois segmentos consecutivos, cada um dos quais contém páginas inteiras alinhadas:
- O primeiro segmento é somente leitura e inclui cabeçalhos ELF, além de dados constantes.
- O segundo segmento é executável e contém código vDSO.
A imagem inteira do vDSO consiste apenas nas páginas desses dois segmentos. Apenas dois valores extraídos dos cabeçalhos ELF são necessários para exibir a memória vDSO: o número de páginas em cada segmento.
Dados constantes do tempo de inicialização do SO
Algumas chamadas do sistema simplesmente retornam valores constantes (os valores devem ser solicitados no tempo de execução e não podem ser compilados no código do modo de usuário). Esses valores são fixos no kernel no momento da compilação ou determinados pelo kernel no momento da inicialização (parâmetros de inicialização e parâmetros de hardware). Por exemplo: zx_system_get_version () , zx_system_get_num_cpus () e zx_ticks_per_second () . O valor de retorno da última função, por exemplo, é afetado pelo parâmetro de linha de comando do kernel .
Espere, o número de CPUs é uma constante?Curiosamente, a descrição da função zx_system_get_num_cpus () também afirma explicitamente que o sistema operacional não oferece suporte à troca a quente do número de processadores:
Esse número não pode ser alterado durante uma execução do sistema, apenas no momento da inicialização.
Isso, pelo menos, indica indiretamente que o sistema operacional não está posicionado como um servidor.
Como esses valores são constantes, não faz sentido pagar por chamadas reais do sistema ao kernel do sistema operacional. Em vez disso, sua implementação são funções simples do C ++ que retornam dados lidos do segmento constante do vDSO. Os valores capturados durante a compilação (como a sequência de versões do sistema) são simplesmente compilados no vDSO.
Para valores determinados no momento da inicialização, o kernel deve modificar o conteúdo do vDSO. Isso é feito usando o código executável inicial que gera o vDSO do VMO antes que o kernel inicie o primeiro processo do usuário (e passa o descritor do VMO). Durante a compilação, as compensações da imagem vDSO ( vdso_constants ) são extraídas do arquivo ELF e depois incorporadas ao kernel. E no momento da inicialização, o kernel exibe temporariamente as páginas que abrangem vdso_constants em seu próprio espaço de endereço para pré-inicializar a estrutura com os valores corretos (para a inicialização atual do sistema).
Por que toda essa dor de cabeça ?
Uma das razões mais importantes é a segurança. Ou seja, se um invasor conseguir executar código arbitrário (shell-), ele precisará usar funções vDSO para chamar funções do sistema. O primeiro obstáculo será a randomização acima mencionada do endereço de inicialização do vDSO para cada processo criado. E como o kernel do sistema operacional é responsável pelo VMO (objeto de memória virtual) do vDSO, ele pode mapear um vDSO completamente diferente para um processo específico, proibindo, assim, chamadas perigosas (e não necessárias para um processo em particular). Por exemplo: você pode impedir que os drivers gerem processos filhos ou manipular áreas projetadas do MMIO. Essa é uma ótima ferramenta para reduzir a superfície do ataque.
Nota: Atualmente, o suporte para vários vDSOs está sendo desenvolvido ativamente. Já existe uma implementação de prova de conceito e testes simples, mas é necessário mais trabalho para melhorar a confiabilidade da implementação e determinar quais opções estão disponíveis. O conceito atual fornece opções de imagem vDSO que exportam apenas um subconjunto da interface de chamada do sistema vDSO completa.
E quanto a outros sistemas operacionais?Note-se que técnicas semelhantes já foram usadas com sucesso em outros sistemas operacionais. Por exemplo, no Windows, há um ProcessSystemCallDisablePolicy :
Chamada de sistema Win32k desabilitar a restrição para restringir a capacidade de usar NTUser e GDI