El 27 de marzo, en Mozilla, anunciamos la estandarización de WASI, la interfaz del sistema WebAssembly (interfaz del sistema WebAssembly).
Por qué: los desarrolladores comenzaron a usar WebAssembly fuera del navegador, porque WASM proporciona una forma rápida, escalable y segura de ejecutar el mismo código en todas las máquinas. Pero todavía no tenemos una base sólida para tal desarrollo. Fuera del navegador, necesita alguna forma de comunicarse con el sistema, es decir, la interfaz del sistema. Pero la plataforma WebAssembly todavía no lo tiene.
Qué: WebAssembly es un ensamblador para una máquina conceptual más que física. Funciona en varias arquitecturas y, por lo tanto, la interfaz del sistema es necesaria para que un SO conceptual funcione en diferentes sistemas operativos.
Esto es lo que es WASI: es una interfaz de sistema para la plataforma WebAssembly.
Nos esforzamos por crear una interfaz de sistema que se convierta en un verdadero compañero para WebAssembly con la máxima portabilidad y seguridad.
Quién: Como parte del equipo de desarrollo de WebAssembly, organizamos un subgrupo que se estandarizará en
WASI . Ya hemos reunido socios interesados y estamos buscando nuevos.
Aquí hay algunas razones por las cuales nosotros, nuestros socios y seguidores consideramos esto importante:
Sean White, Director de I + D de Mozilla:"WebAssembly ya está cambiando la forma en que las personas entregan nuevos tipos de contenido atractivo. Ayuda a los desarrolladores y creadores de contenido. Hasta ahora, todo ha funcionado a través de navegadores, pero con WASI, más usuarios y más dispositivos en diferentes lugares se beneficiarán de WebAssembly ".
Tyler McMullen, CTO rápidamente:“Vemos WebAssembly como una plataforma para ejecutar código de forma rápida y segura en una nube perimetral. A pesar de los diferentes entornos (borde y navegadores), gracias a WASI, no tiene que portar el código a cada plataforma ".
Miles Borins, CTO del Comité Directivo del Nodo:“WebAssembly puede resolver uno de los mayores problemas de Node: cómo lograr una velocidad casi nativa y reutilizar el código escrito en otros lenguajes como C y C ++, manteniendo la portabilidad y la seguridad. La estandarización de WASI es el primer paso hacia esto ”.
Lori Voss, cofundadora de npm:“Npm está extremadamente entusiasmado con el potencial WebAssembly para el ecosistema npm, ya que hace que sea mucho más fácil que el código nativo se ejecute en aplicaciones JavaScript del lado del servidor. Esperamos los resultados de este proceso ".
¡Entonces este es un gran evento!
Actualmente hay tres implementaciones de WASI:
Demostración de WASI en acción:
A continuación, hablaremos sobre la propuesta de Mozilla sobre cómo debería funcionar esta interfaz del sistema.
¿Qué es una interfaz del sistema?
Muchos dicen que lenguajes como C proporcionan acceso directo a los recursos del sistema. Pero esto no es del todo cierto. En la mayoría de los sistemas, estos idiomas no tienen acceso directo a cosas como abrir o crear archivos. Por que no
Debido a que estos recursos del sistema (archivos, memoria y conexiones de red) son demasiado importantes para la estabilidad y la seguridad.
Si un programa arruina accidentalmente los recursos de otro, puede causar un bloqueo. Peor aún, si un programa (o un usuario) invade específicamente los recursos de otras personas, puede robar datos confidenciales.

Por lo tanto, necesita una forma de controlar qué programas y usuarios pueden acceder a los recursos. Durante mucho tiempo, los desarrolladores de sistemas idearon una forma de proporcionar dicho control: anillos de protección.
Con anillos de protección, el SO esencialmente establece una barrera protectora alrededor de los recursos del sistema. Este es el núcleo. Solo puede realizar operaciones como crear un archivo, abrir un archivo o abrir una conexión de red.
Los programas de usuario se ejecutan fuera del núcleo en lo que se llama espacio de usuario. Si el programa quiere abrir el archivo, debe solicitar el núcleo.

Aquí es donde surge el concepto de una llamada al sistema. Cuando un programa necesita pedirle alguna operación al kernel, envía una llamada al sistema. El kernel verifica al usuario que se contacta y ve si tiene permiso para acceder a este archivo.
En la mayoría de los dispositivos, la única forma de acceder a los recursos del sistema es a través de llamadas del sistema.

El sistema operativo proporciona acceso a las llamadas del sistema. Pero si cada sistema operativo tiene sus propias llamadas al sistema, ¿no necesitan escribir diferentes versiones del código? Afortunadamente no. El problema se resuelve usando la abstracción.
La mayoría de los idiomas tienen una biblioteca estándar. Al codificar, el programador no necesita saber para qué sistema escribe. Solo usa la interfaz. Luego, al compilar, su cadena de herramientas elige qué implementación de interfaz usar para qué sistema. Esta implementación utiliza funciones de la API del sistema operativo, por lo que es específica para ella.
Aquí es donde aparece el concepto de una interfaz de sistema. Por ejemplo, si compila
printf
para una máquina con Windows, utilizará la API de Windows. Si se compila para Mac o Linux, usa POSIX.

Sin embargo, esto plantea un problema para WebAssembly. Aquí no sabemos para qué sistema operativo optimizar el programa incluso durante la compilación. Por lo tanto, no puede usar la interfaz del sistema de ningún sistema operativo dentro de la implementación de la biblioteca estándar en WebAssembly.

Ya he dicho que WebAssembly es un
ensamblador para una máquina conceptual , no una máquina real. Del mismo modo, WebAssembly necesita una interfaz de sistema para un sistema operativo conceptual en lugar de real.
Pero ya hay tiempos de ejecución que pueden ejecutar WebAssembly fuera del navegador, incluso sin esta interfaz del sistema. Como lo hacen A ver
¿Cómo funciona WebAssembly ahora fuera del navegador?
La primera herramienta para generar código WebAssembly fue Emscripten. Emula en la web una interfaz específica del sistema operativo: POSIX. Esto significa que el programador puede usar las funciones de la biblioteca estándar de C (libc).
Para esto, Emscripten usa su propia implementación de libc. Se divide en dos partes: la primera se compila en un módulo WebAssembly y la otra se implementa en código JS-glue. Este pegamento JS envía llamadas al navegador que está hablando con el sistema operativo.

La mayor parte del código inicial de WebAssembly se compila con Emscripten. Por lo tanto, cuando las personas comenzaron a querer ejecutar WebAssembly sin un navegador, comenzaron a ejecutar el código Emscripten.
Entonces, en estos tiempos de ejecución, debe crear sus propias implementaciones para todas las funciones que estaban en el código JS-glue.
Pero hay un problema. La interfaz proporcionada por el código de pegamento JS no se ha diseñado como una interfaz estándar o incluso pública. Por ejemplo, para llamar como
read
en la API normal, el código de pegamento JS usa la
_system3(which, varargs)
.

El primer parámetro
which
es un número entero que siempre coincide con el número en el nombre (en nuestro caso 3).
El segundo parámetro,
varargs
enumera los argumentos. Se llama
varargs
porque podemos tener un número diferente de argumentos. Pero WebAssembly no permite pasar un número variable de argumentos a una función. Por lo tanto, se transmiten a través de la memoria lineal, que es insegura y más lenta que a través de registros.
Para Emscripten en el navegador, esto es normal. Pero ahora los tiempos de ejecución ven esto como un estándar de facto, implementando sus propias versiones de pegamento JS. Emulan los detalles internos de la capa de emulación POSIX.
Esto significa que vuelven a implementar el código (por ejemplo, pasan argumentos como valores de almacenamiento dinámico), lo que tiene sentido dadas las restricciones de Emscripten, pero no existen tales restricciones en estos entornos de tiempo de ejecución.

Si hemos estado construyendo el ecosistema de WebAssembly en las próximas décadas, necesita una base sólida, no muletas. Esto significa que nuestro estándar real no puede ser la emulación de emulación.
Pero, ¿qué principios se aplican en este caso?
¿A qué principios debe adherirse la interfaz del sistema WebAssembly?
Dos principios fundamentales de WebAssembly:
Vamos más allá del navegador, pero conservamos estos principios clave.
Sin embargo, el enfoque POSIX y el sistema de control de acceso Unix no nos dan el resultado deseado. Veamos cuál es el problema.
Portabilidad
POSIX proporciona portabilidad del código fuente. Puede compilar el mismo código fuente con diferentes versiones de libc para diferentes computadoras.

Pero WebAssembly debe ir más allá de eso. Necesitamos compilar una vez para ejecutar en un montón de sistemas diferentes. Necesitamos binarios portátiles.

Esto simplifica la distribución del código.
Por ejemplo, si los módulos de nodo nativos están escritos en WebAssembly, los usuarios no necesitan ejecutar node-gyp al instalar aplicaciones con módulos nativos, y los desarrolladores no necesitan configurar y distribuir docenas de archivos binarios.
Seguridad
Cuando el código le pide al sistema operativo que realice entradas o salidas, el sistema operativo debe evaluar la seguridad de esta operación, generalmente utilizando un sistema de control de acceso basado en la propiedad y los grupos.
Por ejemplo, un programa solicita abrir un archivo. El usuario tiene un conjunto específico de archivos a los que tiene acceso.
Cuando un usuario inicia un programa, el programa se inicia en nombre de ese usuario. Si el usuario tiene acceso al archivo, ya sea que es su propietario o es parte de un grupo que tiene acceso al archivo, entonces el programa tiene el mismo acceso.

Esto protege a los usuarios unos de otros, lo que tenía sentido en los viejos tiempos, cuando mucha gente trabajaba en una computadora y los administradores controlaban el software. Entonces, la principal amenaza era que otros usuarios miraran sus archivos.
Todo ha cambiado Actualmente, los sistemas suelen ser de un solo usuario, pero utilizan códigos de terceros de confiabilidad desconocida. Ahora la principal amenaza proviene del código que ejecuta usted mismo.
Por ejemplo, para la biblioteca de su aplicación, se ha iniciado un nuevo responsable de mantenimiento (como suele ser el caso en código abierto). Puede ser un activista sincero ... o un intruso. Y si tiene acceso a su sistema, por ejemplo, la capacidad de abrir cualquier archivo y enviarlo a través de la red, entonces este código puede causar un gran daño.
Aplicación sospechosa : trabajo para el usuario Bob. ¿Puedo abrir su billetera Bitcoin?
Núcleo : ¿Para Bob? Por supuesto!
Aplicación sospechosa : ¡Genial! ¿Qué pasa con la conectividad de red?Es por eso que usar bibliotecas de terceros es peligroso. WebAssembly proporciona seguridad de una manera diferente, a través del entorno limitado. Aquí, el código no puede hablar directamente con el sistema operativo. Pero entonces, ¿cómo acceder a los recursos del sistema? Las funciones de sandbox de host (el navegador o el tiempo de ejecución de wasm) que el código puede usar.
Esto significa que el host limita programáticamente la funcionalidad del programa, no permitiéndole simplemente actuar en nombre del usuario, provocando cualquier llamada al sistema con todos los derechos del usuario.
Tener un sandbox en sí mismo no hace que el sistema sea seguro: el host aún puede transferir la funcionalidad completa al sandbox, en cuyo caso no proporciona ninguna protección. Pero el entorno limitado ofrece al menos una oportunidad teórica para que los hosts creen un sistema más seguro.
WA : Por favor, aquí hay algunos juguetes seguros para interactuar con el sistema operativo (safe_write, safe_read).
Aplicación sospechosa : Oh, maldición ... ¿dónde está mi acceso a la red?En cualquier interfaz del sistema, debe cumplir con estos dos principios. La portabilidad facilita el desarrollo y la distribución de software, y las herramientas para proteger al host y los usuarios son absolutamente necesarias.
¿Cómo debería ser esa interfaz de sistema?
Dados estos dos principios clave, ¿cuál debería ser la interfaz del sistema WebAssembly?
Esto lo descubriremos en el proceso de estandarización. Sin embargo, tenemos una sugerencia para empezar:
- Crear un conjunto modular de interfaces estándar
- Comencemos estandarizando el módulo central wasi-core.

¿Qué habrá en wasi-core? Estos son los elementos básicos que necesitan todos los programas. El módulo cubrirá la mayor parte de la funcionalidad POSIX, incluidos archivos, conexiones de red, relojes y números aleatorios.
Gran parte de la funcionalidad básica requerirá un enfoque muy similar. Por ejemplo, se proporciona un enfoque orientado a archivos POSIX con llamadas abiertas, cerradas, leídas y escritas al sistema, y todo lo demás son complementos de arriba.
Pero wasi-core no cubre toda la funcionalidad POSIX. Por ejemplo, el concepto de un proceso no encaja claramente en WebAssembly. Además, está claro que cada motor de WebAssembly debe admitir operaciones de proceso como
fork
. Pero también queremos hacer posible la estandarización de la
fork
.

Idiomas como Rust usarán wasi-core directamente en sus bibliotecas estándar. Por ejemplo,
open
from Rust se implementa al compilar en WebAssembly llamando a
__wasi_path_open
.
Para C y C ++, creamos
wasi-sysroot , que implementa libc en términos de funciones wasi-core.

Esperamos que compiladores como Clang puedan interactuar con la API de WASI, y cadenas de herramientas completas como el compilador Rust y Emscripten usarán WASI como parte de sus implementaciones de sistema.
¿Cómo invoca el código personalizado estas funciones WASI?
El tiempo de ejecución en el que se ejecuta el código pasa la función wasi-core, colocando el objeto en la caja de arena.

Esto proporciona portabilidad, porque cada host puede tener su propia implementación wasi-core específicamente para su plataforma: desde tiempos de ejecución de WebAssembly como Mozilla Wasmtime y Fastly Lucet, hasta Node o incluso un navegador.
También proporciona un aislamiento confiable, porque el host selecciona en función del software qué funciones wasi-core se transferirán al entorno limitado, es decir, qué llamadas al sistema debería permitir. Esto es seguridad.

WASI mejora y extiende la seguridad al introducir un concepto de seguridad basado en autorizaciones en el sistema.
Por lo general, si el código necesita abrir el archivo, se
open
con el nombre de la ruta en la línea. Luego, el sistema operativo comprueba si el código tiene derecho a dicha acción (en función de los derechos del usuario que inició el programa).
En el caso de WASI, al llamar a una función para acceder a un archivo, debe pasar un descriptor de archivo al que se adjuntan los permisos para el archivo en sí o para el directorio que contiene el archivo.
Por lo tanto, no puede tener código que accidentalmente le pida que abra
/etc/passwd
. En cambio, el código solo puede funcionar con sus propios directorios.

Esto permite que varias llamadas del sistema se resuelvan de forma segura en el código aislado porque las capacidades de estas llamadas del sistema son limitadas.
Y así en cada módulo. Por defecto, el módulo no tiene acceso a los descriptores de archivo. Pero si el código en un módulo tiene un descriptor de archivo, puede pasarlo a funciones llamadas en otros módulos. O cree versiones más limitadas del descriptor de archivo para pasar a otras funciones.
Por lo tanto, el tiempo de ejecución pasa los descriptores de archivos que la aplicación puede usar en el código de nivel superior, y luego los descriptores de archivos se distribuyen en el resto del sistema según sea necesario.

Esto acerca a WebAssembly al principio del mínimo privilegio, donde el módulo solo obtiene acceso al conjunto mínimo de recursos necesarios para hacer su trabajo.
Este concepto se basa en la seguridad basada en privilegios, como en CloudABI y Capsicum. Uno de los problemas con estos sistemas es la difícil portabilidad del código. Pero creemos que este problema puede resolverse.
Si el código ya usa
openat
con rutas de archivo relativas, compilar el código simplemente funcionará.
Si el código usa la migración
open
y de estilo
open
es demasiado drástica, WASI proporcionará una solución incremental. Con
libpreopen, crea una lista de rutas de archivos a las que la aplicación tiene acceso legal. Luego use
open
, pero solo con estos caminos.
Que sigue
Creemos que wasi-core es un buen comienzo. Conserva la portabilidad y seguridad de WebAssembly, proporcionando una base sólida para el ecosistema.
Pero después de la estandarización completa de wasi-core, se deben resolver otros problemas, que incluyen:
- entrada-salida asíncrona
- monitoreo de archivos
- bloqueo de archivo
Esto es solo el comienzo, así que si tienes alguna idea, ¡
participa !