En el marco de los esfuerzos para fortalecer la seguridad de los componentes de software críticos de la plataforma Android, Google ha reescrito en Rust el firmware pvmfm, utilizado para organizar el funcionamiento de máquinas virtuales iniciadas por el hipervisor pVM dentro del Android Virtualization Framework. Anteriormente, el firmware estaba escrito en C y se implementaba sobre el cargador U-Boot, en cuyo código se habían encontrado anteriormente vulnerabilidades causadas por problemas de manejo de memoria.
El firmware reescrito en Rust está incluido en Android 14, y las bibliotecas universales creadas durante el desarrollo del firmware se han expresado en forma de paquetes crate y se han transferido a la comunidad Rust. Por ejemplo, se ha publicado el paquete smccc para soportar las interfaces ARM PSCI (Power State Coordination Interface) y las llamadas SMCCC (SMC Calling Convention), y el paquete aarch64-paging para manipular tablas de páginas de memoria. También se ha trabajado en la corrección de errores y la expansión de la funcionalidad del paquete existente virtio-drivers con la implementación de controladores VirtIO. Además de la plataforma Android, los paquetes mencionados se utilizan en el proyecto Oak, que desarrolla componentes para la transmisión, almacenamiento y procesamiento de datos en entornos seguros (TEE, Trusted Execution Environment).
El hipervisor pVM obtiene el control en una etapa temprana del arranque y garantiza el aislamiento completo de la memoria máquinas virtuales y del entorno host, evitando que el sistema host acceda a las máquinas virtuales protegidas, donde se procesan datos confidenciales. El firmware pvmfm (Protected Virtual Machine Firmware) obtiene el control inmediatamente después de que se inicia la máquina virtual, realiza una verificación del entorno formado y toma una decisión sobre la interrupción de emergencia del arranque en caso de detectar problemas de integridad o genera un certificado de arranque para el sistema huésped si se confirma la cadena de confianza.
La reescritura en el lenguaje Rust permite una adherencia más simple y segura a la "regla de los dos", aplicada por Google para mantener la seguridad de los componentes del sistema Android. De acuerdo con esta regla, cualquier código agregado no debe cumplir más de dos condiciones de tres: trabajar con datos de entrada no verificados, usar un lenguaje de programación no seguro (C/C++) y ejecutarse con privilegios elevados. De esta regla se deduce que el código para manejar datos externos debe ser o bien reducido a los privilegios mínimos (aislado) o escrito en un lenguaje de programación seguro. Según estadísticas de Google, aproximadamente el 70% de todas las vulnerabilidades peligrosas identificadas en Android son causadas por errores en el manejo de la memoria.
Rust se centra en el manejo seguro de la memoria y permite reducir el riesgo de apariciones de vulnerabilidades provocadas por problemas como el acceso a áreas de memoria después de su liberación y el desbordamiento de búfer. El manejo seguro de la memoria en Rust se garantiza durante la compilación a través de la verificación de referencias, el seguimiento de la propiedad de los objetos y el control del tiempo de vida de los objetos (áreas de visibilidad), así como mediante la evaluación de la corrección del acceso a la memoria durante la ejecución del código. Rust también proporciona herramientas para proteger contra desbordamientos numéricos, requiere la inicialización obligatoria de los valores de las variables antes de su uso, maneja mejor los errores en la biblioteca estándar, aplica la concepción de inmutabilidad (immutable) de referencias y variables por defecto, y ofrece una fuerte tipificación estática para minimizar errores lógicos.
Entre las dificultades que surgen al desarrollar componentes de bajo nivel en el lenguaje Rust, como controladores, se menciona la necesidad de trabajar con punteros desnudos en modo unsafe, dado que Rust está diseñado con la atención en el uso de la memoria asignada en el programa, y en código que opera sin capas sobre el hardware, se debe acceder a la memoria compartida y a MMIO. Actualmente, las capacidades de Rust para trabajar con punteros desnudos dejan mucho que desear, pero la situación debería cambiar tras la estabilización del soporte para los macros offset_of, slice_ptr_get y slice_ptr_len.
Entre las desventajas, merece la pena mencionar la necesidad de una sintaxis mejorada para acceder a los campos de estructuras e índices de matrices a través de punteros desnudos sin crear referencias, así como las limitaciones en la creación de envolturas seguras sobre operaciones inseguras que pueden provocar comportamientos indeterminados y que no pueden ser verificadas por el compilador. Por ejemplo, tal envoltura no se puede crear para las operaciones con tablas de páginas de memoria, ya que el mapeo de páginas en una parte del programa puede influir en otras partes.
En cuanto al tamaño del código resultante, la antigua versión del firmware pVM ocupaba 220 kB, mientras que la nueva ocupa 460 kB; sin embargo, en la versión reescrita se añadieron nuevas funcionalidades, gracias a las cuales se logró deshacerse de algunos otros componentes utilizados durante el arranque. En definitiva, el tamaño total de todos los componentes de arranque antiguos y nuevos resultó ser comparable. Se destaca que cuando el tamaño es más importante que el rendimiento, se pueden lograr resultados comparables al lenguaje C al incluir en el compilador modos adicionales de optimización por tamaño, eliminando dependencias innecesarias y no utilizando herramientas de formateo de cadenas.
Además, se menciona la continuación del trabajo en la implementación de la posibilidad de ejecutar aplicaciones confiables (Trusted Application) escritas en Rust en el sistema operativo Trusty, que proporciona un entorno de ejecución confiable (Trusted Execution Environment) para Android, funcionando en paralelo con Android en el mismo procesador en un entorno aislado separado. Trusty se utiliza en dispositivos Pixel y ya emplea Rust en bibliotecas y componentes del sistema (el núcleo permanece en C).
Fuente: opennet.ru
