WASI Standard: Iniciar o WebAssembly Além da Web

Em 27 de março, anunciamos na Mozilla a padronização da WASI, a interface do sistema WebAssembly (interface do sistema WebAssembly).

Motivo: os desenvolvedores começaram a usar o WebAssembly fora do navegador, porque o WASM fornece uma maneira rápida, escalável e segura de executar o mesmo código em todas as máquinas. Mas ainda não temos uma base sólida para esse desenvolvimento. Fora do navegador, você precisa de alguma maneira de se comunicar com o sistema, ou seja, a interface do sistema. Mas a plataforma WebAssembly ainda não a possui.

O quê: O WebAssembly é um montador de uma máquina conceitual e não física. Funciona em várias arquiteturas; portanto, é necessária uma interface do sistema para que um SO conceitual funcione em diferentes sistemas operacionais.

Aqui está o que o WASI é: é uma interface do sistema para a plataforma WebAssembly.

Nós nos esforçamos para criar uma interface do sistema que se tornará uma verdadeira companheira do WebAssembly com portabilidade e segurança máximas.

Quem: Como parte da equipe de desenvolvimento do WebAssembly, organizamos um subgrupo que padronizará o WASI . Já reunimos parceiros interessados ​​e estamos procurando novos.

Aqui estão algumas razões pelas quais nós, nossos parceiros e apoiadores consideramos isso importante:

Sean White, diretor de P&D da Mozilla:
"O WebAssembly já está mudando a maneira como as pessoas entregam novos tipos de conteúdo atraente. Ajuda os desenvolvedores e criadores de conteúdo. Até o momento, tudo funcionou através dos navegadores, mas com o WASI, mais usuários e mais dispositivos em locais diferentes se beneficiarão do WebAssembly. ”

Tyler McMullen, CTO Fastly:
“Vemos o WebAssembly como uma plataforma para a execução rápida e segura de código em uma nuvem de borda. Apesar dos diferentes ambientes (borda e navegadores), graças ao WASI, você não precisa portar o código para cada plataforma ".

Miles Borins, CTO do Comitê Diretor do Nó:
“O WebAssembly pode resolver um dos maiores problemas do Node: como obter velocidade quase nativa e reutilizar códigos escritos em outros idiomas, como C e C ++, mantendo a portabilidade e a segurança. A padronização WASI é o primeiro passo para isso. ”

Lori Voss, cofundadora da npm:
“O Npm está extremamente empolgado com os recursos potenciais do WebAssembly do ecossistema npm, pois facilita muito a execução do código nativo nos aplicativos JavaScript do servidor. Estamos ansiosos pelos resultados desse processo. ”

Portanto, este é um grande evento!

Atualmente, existem três implementações WASI:


Demonstração WASI em ação:


Em seguida, falaremos sobre a proposta da Mozilla sobre como essa interface do sistema deve funcionar.

O que é uma interface do sistema?


Muitos dizem que idiomas como C fornecem acesso direto aos recursos do sistema. Mas isso não é inteiramente verdade. Na maioria dos sistemas, esses idiomas não têm acesso direto a coisas como abrir ou criar arquivos. Porque não

Porque esses recursos do sistema - arquivos, memória e conexões de rede - são importantes demais para estabilidade e segurança.

Se um programa acidentalmente arruinar os recursos de outro, poderá causar uma falha. Pior, se um programa (ou um usuário) invadir especificamente os recursos de outras pessoas, poderá roubar dados confidenciais.



Portanto, você precisa de uma maneira de controlar quais programas e usuários podem acessar recursos. Por um longo tempo, os desenvolvedores de sistemas criaram uma maneira de fornecer esse controle: anéis de proteção.

Com anéis de proteção, o sistema operacional essencialmente estabelece uma barreira protetora em torno dos recursos do sistema. Esse é o núcleo. Somente ele pode executar operações como criar um arquivo, abrir um arquivo ou abrir uma conexão de rede.

Os programas do usuário são executados fora do kernel no que é chamado de espaço do usuário. Se o programa quiser abrir o arquivo, ele deverá solicitar o kernel.



É aqui que surge o conceito de uma chamada do sistema. Quando um programa precisa solicitar alguma operação ao kernel, ele envia uma chamada de sistema. O kernel verifica o usuário que está entrando em contato e vê se ele tem permissão para acessar esse arquivo.

Na maioria dos dispositivos, a única maneira de acessar os recursos do sistema é através de chamadas do sistema.



O sistema operacional fornece acesso às chamadas do sistema. Mas se cada sistema operacional tiver suas próprias chamadas de sistema, eles não precisam escrever versões diferentes do código? Felizmente não. O problema é resolvido usando abstração.

A maioria dos idiomas possui uma biblioteca padrão. Ao codificar, o programador não precisa saber para qual sistema ele escreve. Apenas usa a interface. Em seguida, ao compilar, sua cadeia de ferramentas escolhe qual implementação de interface usar para qual sistema. Essa implementação usa funções da API do sistema operacional, portanto é específica a ela.

É aqui que o conceito de uma interface do sistema aparece. Por exemplo, se você compilar printf para uma máquina Windows, ela usará a API do Windows. Se compilado para Mac ou Linux, ele usa POSIX.



No entanto, isso representa um problema para o WebAssembly. Aqui não sabemos para qual SO otimizar o programa, mesmo durante a compilação. Portanto, você não pode usar a interface do sistema de nenhum SO dentro da implementação da biblioteca padrão no WebAssembly.



Eu já disse que o WebAssembly é um montador de uma máquina conceitual , não uma máquina real. Da mesma forma, o WebAssembly precisa de uma interface do sistema para um sistema operacional conceitual, e não real.

Mas já existem tempos de execução que podem executar o WebAssembly fora do navegador, mesmo sem essa interface do sistema. Como eles fazem isso? Vamos ver

Como o WebAssembly agora funciona fora do navegador?


A primeira ferramenta para gerar o código do WebAssembly foi o Emscripten. Emula na web uma interface específica do sistema OS - POSIX. Isso significa que o programador pode usar as funções da biblioteca C padrão (libc).

Para isso, o Emscripten usa sua própria implementação libc. Ele é dividido em duas partes: a primeira é compilada em um módulo WebAssembly e a outra é implementada no código de cola JS. Essa cola JS envia chamadas para o navegador que está falando com o sistema operacional.



A maior parte do código inicial do WebAssembly é compilada com o Emscripten. Portanto, quando as pessoas começaram a querer executar o WebAssembly sem um navegador, começaram a executar o código Emscripten.

Portanto, nesses tempos de execução, você deve criar suas próprias implementações para todas as funções que estavam no código JS-cola.

Mas há um problema. A interface fornecida pelo código de cola JS não foi projetada como uma interface padrão ou pública. Por exemplo, para chamar como read na API normal, o código de cola JS usa a _system3(which, varargs) .



O primeiro parâmetro which é um número inteiro que sempre corresponde ao número no nome (no nosso caso 3).

O segundo parâmetro, varargs lista os argumentos. Isso se chama varargs porque podemos ter um número diferente de argumentos. Mas o WebAssembly não permite passar um número variável de argumentos para uma função. Portanto, eles são transmitidos através da memória linear, que é insegura e mais lenta do que através dos registradores.

Para o Emscripten no navegador, isso é normal. Mas agora os tempos de execução veem isso como um padrão de fato, implementando suas próprias versões da cola JS. Eles emulam os detalhes internos da camada de emulação POSIX.

Isso significa que eles reimplementam o código (por exemplo, passam argumentos como valores de heap), que faziam sentido devido às restrições de Emscripten, mas não existem restrições nesses ambientes de tempo de execução.



Se estivermos construindo o ecossistema do WebAssembly nas próximas décadas, ele precisará de uma base sólida, não de muletas. Isso significa que nosso padrão atual não pode ser emulação de emulação.

Mas que princípios se aplicam neste caso?

Quais princípios a interface do sistema WebAssembly deve aderir?


Dois princípios fundamentais do WebAssembly:

  • portabilidade
  • segurança

Vamos além do navegador, mas mantemos esses princípios fundamentais.

No entanto, a abordagem POSIX e o sistema de controle de acesso Unix não nos dão o resultado desejado. Vamos ver qual é o problema.

Portabilidade


O POSIX fornece portabilidade do código fonte. Você pode compilar o mesmo código fonte com versões diferentes da libc para computadores diferentes.



Mas o WebAssembly deve ir além disso. Precisamos compilar uma vez para rodar em vários sistemas diferentes. Precisamos de binários portáteis.



Isso simplifica a distribuição de código.

Por exemplo, se os módulos Nó nativos são gravados no WebAssembly, os usuários não precisam executar o node-gyp ao instalar aplicativos com módulos nativos, e os desenvolvedores não precisam configurar e distribuir dezenas de arquivos binários.

Segurança


Quando o código solicita que o sistema operacional faça entrada ou saída, o sistema operacional deve avaliar a segurança dessa operação, geralmente usando um sistema de controle de acesso baseado na propriedade e nos grupos.

Por exemplo, um programa pede para abrir um arquivo. O usuário possui um conjunto específico de arquivos aos quais ele tem acesso.

Quando um usuário inicia um programa, o programa inicia em nome desse usuário. Se o usuário tiver acesso ao arquivo - ele é o proprietário ou faz parte de um grupo que tem acesso ao arquivo -, o programa terá o mesmo acesso.



Isso protege os usuários uns dos outros, o que fazia sentido nos velhos tempos, quando muitas pessoas trabalhavam em um computador e os administradores controlavam o software. Em seguida, a principal ameaça foram outros usuários que examinaram seus arquivos.

Tudo mudou. Atualmente, os sistemas geralmente são de usuário único, mas usam código de terceiros de confiabilidade desconhecida. Agora, a principal ameaça vem do código que você mesmo executa.

Por exemplo, para a biblioteca em seu aplicativo, um novo mantenedor foi iniciado (como costuma ser o caso em código aberto). Ele pode ser um ativista sincero ... ou um intruso. E se ele tiver acesso ao seu sistema (por exemplo, a capacidade de abrir qualquer arquivo e enviá-lo pela rede), esse código poderá causar grandes danos.


Aplicativo suspeito : trabalho para o usuário Bob. Posso abrir a carteira do Bitcoin?
Núcleo : Para Bob? Claro!
Aplicativo suspeito : Ótimo! E a conectividade de rede?

É por isso que o uso de bibliotecas de terceiros é perigoso. O WebAssembly fornece segurança de uma maneira diferente - através da sandbox. Aqui, o código não pode falar diretamente com o sistema operacional. Mas então como acessar os recursos do sistema? As caixas de proteção do host (o navegador ou o wasm runtime) funcionam que o código pode usar.

Isso significa que o host em uma base de software limita a funcionalidade do programa, não permitindo que você simplesmente aja em nome do usuário, causando chamadas ao sistema com todos os direitos.

Ter uma caixa de proteção por si só não torna o sistema seguro - o host ainda pode transferir toda a funcionalidade para a caixa de proteção; nesse caso, ele não fornece nenhuma proteção. Mas o sandbox fornece pelo menos uma oportunidade teórica para os hosts criarem um sistema mais seguro.


WA : Por favor, aqui estão alguns brinquedos seguros para interagir com o sistema operacional (safe_write, safe_read).
Aplicativo suspeito : Oh, droga ... onde está o meu acesso à rede?

Em qualquer interface do sistema, você deve aderir a esses dois princípios. A portabilidade facilita o desenvolvimento e a distribuição de software, e são absolutamente necessárias ferramentas para proteger o host e os usuários.

Como deve ser essa interface do sistema?


Dados esses dois princípios fundamentais, qual deve ser a interface do sistema WebAssembly?

Isso descobriremos no processo de padronização. No entanto, temos uma sugestão para começar:

  • Criando um conjunto modular de interfaces padrão
  • Vamos começar padronizando o módulo principal wasi-core.




O que haverá no wasi-core? Estes são os princípios necessários para todos os programas. O módulo cobrirá a maioria das funcionalidades do POSIX, incluindo arquivos, conexões de rede, relógios e números aleatórios.

Grande parte da funcionalidade básica exigirá uma abordagem muito semelhante. Por exemplo, uma abordagem orientada a arquivos POSIX é fornecida com chamadas de sistema de abrir, fechar, ler e gravar, e todo o resto são complementos de cima.

Mas o wasi-core não cobre toda a funcionalidade POSIX. Por exemplo, o conceito de um processo não se encaixa claramente no WebAssembly. Além disso, é claro que todo mecanismo do WebAssembly deve suportar operações de processo, como fork . Mas também queremos tornar possível a padronização dos fork .



Idiomas como o Rust usarão o wasi-core diretamente em suas bibliotecas padrão. Por exemplo, o open from Rust é implementado ao compilar no WebAssembly chamando __wasi_path_open .

Para C e C ++, criamos wasi-sysroot , que implementa libc em termos de funções wasi-core.



Esperamos que compiladores como o Clang possam interagir com a API WASI, e cadeias completas de ferramentas como o compilador Rust e o Emscripten usarão o WASI como parte de suas implementações de sistema.

Como o código customizado chama essas funções WASI?

O tempo de execução no qual o código é executado passa a função wasi-core, colocando o objeto na sandbox.



Isso fornece portabilidade, porque cada host pode ter sua própria implementação wasi-core especificamente para sua plataforma: desde tempos de execução do WebAssembly como Mozilla Wasmtime e Fastly Lucet, até Node ou até um navegador.

Ele também fornece isolamento confiável, porque o host seleciona, em uma base de software, quais funções principais do núcleo são transferidas para a sandbox, ou seja, quais chamadas do sistema devem permitir. Isso é segurança.



O WASI aprimora e estende a segurança, introduzindo um conceito de segurança baseado em autorização no sistema.

Normalmente, se o código precisar abrir o arquivo, ele open com o nome do caminho na linha. Em seguida, o sistema operacional verifica se o código tem direito a essa ação (com base nos direitos do usuário que iniciou o programa).

No caso do WASI, ao chamar uma função para acessar um arquivo, você deve passar um descritor de arquivo ao qual as permissões estão anexadas para o próprio arquivo ou para o diretório que contém o arquivo.

Portanto, você não pode ter um código que solicite acidentalmente que você abra /etc/passwd . Em vez disso, o código pode funcionar apenas com seus próprios diretórios.



Isso permite que várias chamadas do sistema sejam resolvidas com segurança para o código isolado, porque os recursos dessas chamadas do sistema são limitados.

E assim em cada módulo. Por padrão, o módulo não tem acesso aos descritores de arquivo. Mas se o código em um módulo tiver um descritor de arquivo, ele poderá transmiti-lo para funções chamadas em outros módulos. Ou crie versões mais limitadas do descritor de arquivo para passar para outras funções.

Portanto, o tempo de execução passa os descritores de arquivo que o aplicativo pode usar no código de nível superior e, em seguida, os descritores de arquivo são distribuídos pelo resto do sistema, conforme necessário.



Isso aproxima o WebAssembly do princípio do menor privilégio, onde o módulo obtém acesso apenas ao conjunto mínimo de recursos necessários para realizar seu trabalho.

Esse conceito é baseado na segurança baseada em privilégios, como no CloudABI e Capsicum. Um dos problemas com esses sistemas é a difícil portabilidade do código. Mas acreditamos que esse problema pode ser resolvido.

Se o código já usa openat com caminhos de arquivo relativos, a compilação do código funcionará.

Se o código usar a migração open e no estilo openat for muito drástica, o WASI fornecerá uma solução incremental. Usando o libpreopen, você cria uma lista de caminhos de arquivo aos quais o aplicativo tem acesso legal. Em seguida, use open , mas apenas com esses caminhos.

O que vem a seguir?


Acreditamos que o wasi-core é um bom começo. Ele mantém a portabilidade e a segurança do WebAssembly, fornecendo uma base sólida para o ecossistema.

Porém, após a padronização completa do wasi-core, outros problemas precisam ser resolvidos, incluindo:

  • entrada-saída assíncrona
  • monitoramento de arquivos
  • bloqueio de arquivo

Este é apenas o começo; portanto, se você tiver alguma idéia, participe !

Source: https://habr.com/ru/post/pt446764/


All Articles